80 votes

Dupliquer le code en utilisant c ++11

Je suis actuellement en train de travailler sur un projet et j'ai le problème suivant.

J'ai un C++ méthode que je veux travailler de deux façons différentes :

void MyFunction()
{
  foo();
  bar();
  foobar();
}

void MyFunctionWithABonus()
{
  foo();
  bar();
  doBonusStuff();
  foobar();
}

Et je voudrais de ne pas dupliquer mon code parce que la fonction réelle est beaucoup plus long. Le problème est que je ne doit pas en aucun cas ajouter de temps d'exécution du programme lorsque la fonction MyFunction est appelée à la place de MyFunctionWithABonus. C'est pourquoi je ne peux pas juste avoir un paramètre booléen qui j'ai vérifier avec un C++ comparaison.

Mon idée aurait été d'utiliser des modèles C++ pour pratiquement dupliquer mon code, mais je ne peux pas penser à une manière de faire que je n'ai pas d'exécution supplémentaire de temps et je n'ai pas de dupliquer le code.

Je ne suis pas un expert avec des modèles, donc j'ai peut-être raté quelque chose.

Ne l'un de vous a une idée? Ou est-ce tout simplement impossible en C++11?

128voto

Gibet Points 1001

Quelque chose comme ça fera très bien l'affaire:

template<bool bonus = false>
void MyFunction()
{
  foo();
  bar();
  if (bonus) { doBonusStuff(); }
  foobar();
}

Appeler par l'intermédiaire de:

MyFunction<true>();
MyFunction<false>();
MyFunction(); // Call myFunction with the false template by default

Le "laid" modèle peut être évité en ajoutant quelques jolis emballages pour les fonctions:

void MyFunctionAlone() { MyFunction<false>(); }
void MyFunctionBonus() { MyFunction<true>(); }

Vous pouvez trouver quelques belles informations sur cette technique . C'est un "vieux" papier, mais la technique en elle-même rester tout à fait raison.

À condition que vous ayez accès à une belle C++17 compilateur, vous pouvez même pousser plus loin la technique, à l'aide de la constexpr si, comme ça:

template <int bonus>
auto MyFunction() {
  foo();
  bar();
  if      constexpr (bonus == 0) { doBonusStuff1(); }
  else if constexpr (bonus == 1) { doBonusStuff2(); }
  else if constexpr (bonus == 2) { doBonusStuff3(); }
  else if constexpr (bonus == 3) { doBonusStuff4(); }
  // Guarantee that this function will not compile
  // if a bonus different than 0,1,2,3 is passer
  else { static_assert(false);}, 
  foorbar();
}

55voto

Jarod42 Points 15729

Avec template et lambda, vous pouvez faire:

 template <typename F>
void common(F f)
{
  foo();
  bar();
  f();
  foobar();
}

void MyFunction()
{
    common([](){});
}

void MyFunctionWithABonus()
{
  common(&doBonusStuff);
}
 

ou bien vous pouvez simplement créer la fonction prefix et suffix .

 void prefix()
{
  foo();
  bar();
}

void suffix()
{
    foobar();
}

void MyFunction()
{
    prefix();
    suffix();
}

void MyFunctionWithABonus()
{
    prefix();
    doBonusStuff();
    suffix();
}
 

27voto

Cornstalks Points 9261

Compte tenu de certains commentaires que l'OP a fait concernant le débogage, voici une version qui appelle doBonusStuff() pour les versions de débogage, mais pas les versions release (qui définissent NDEBUG):

#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif

void MyFunctionWithABonus()
{
  foo();
  bar();
  DEBUG(doBonusStuff());
  foobar();
}

Vous pouvez également utiliser l' assert macro si vous souhaitez vérifier une condition et d'échouer si elle est fausse (mais uniquement pour les versions de débogage; release n'effectuera pas de la case).

Attention, si doBonusStuff() a des effets secondaires, comme ces effets secondaires ne sera pas présent dans les versions release et peut invalider les hypothèses formulées dans le code.

18voto

Chris Drew Points 1743

Voici une légère variation de la réponse de Jarod42 à l'aide de modèles variadiques afin que l'appelant puisse fournir des fonctions bonus zéro ou un:

 void callBonus() {}

template<typename F>
void callBonus(F&& f) { f(); }

template <typename ...F>
void MyFunction(F&&... f)
{
  foo();
  bar();
  callBonus(std::forward<F>(f)...);
  foobar();
}
 

Code d'appel:

 MyFunction();
MyFunction(&doBonusStuff);
 

11voto

Sebastian Stern Points 592

Une autre version, utilisant uniquement des modèles et aucune fonction de redirection, puisque vous avez indiqué que vous ne souhaitiez aucune surcharge d'exécution. En ce qui me concerne, cela ne fait qu'augmenter le temps de compilation:

 #include <iostream>

using namespace std;

void foo() { cout << "foo\n"; };
void bar() { cout << "bar\n"; };
void bak() { cout << "bak\n"; };

template <bool = false>
void bonus() {};

template <>
void bonus<true>()
{
    cout << "Doing bonus\n";
};

template <bool withBonus = false>
void MyFunc()
{
    foo();
    bar();
    bonus<withBonus>();
    bak();
}

int main(int argc, const char* argv[])
{
    MyFunc();
    cout << "\n";
    MyFunc<true>();
}

output:
foo
bar
bak

foo
bar
Doing bonus
bak
 

Il n'y a maintenant qu'une seule version de MyFunc() avec le paramètre bool tant qu'argument de modèle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X