Dupliquer du code en utilisant c ++ 11

Je travaille actuellement sur un projet et j’ai le problème suivant.

J’ai une méthode C ++ que je veux utiliser de deux manières différentes:

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

Et je ne voudrais pas dupliquer mon code car la fonction réelle est beaucoup plus longue. Le problème est que je ne dois en aucun cas append du temps d’exécution au programme lorsque MyFunction est appelée à la place de MyFunctionWithABonus. C’est pourquoi je ne peux pas avoir un paramètre booléen que je vérifie avec une comparaison C ++.

Mon idée aurait été d’utiliser des modèles C ++ pour dupliquer virtuellement mon code, mais je ne peux pas penser à une façon de faire dans laquelle je n’ai pas de temps d’exécution supplémentaire et je n’ai pas à dupliquer le code.

Je ne suis pas un expert avec des modèles, donc il se peut que je manque quelque chose.

Est-ce que l’un de vous a une idée? Ou est-ce juste impossible en C ++ 11?

Avec modèle et lambda, vous pouvez faire:

 template  void common(F f) { foo(); bar(); f(); foobar(); } void MyFunction() { common([](){}); } void MyFunctionWithABonus() { common(&doBonusStuff); } 

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

 void prefix() { foo(); bar(); } void suffix() { foobar(); } void MyFunction() { prefix(); suffix(); } void MyFunctionWithABonus() { prefix(); doBonusStuff(); suffix(); } 

Quelque chose comme ça fera bien:

 template void MyFunction() { foo(); bar(); if (bonus) { doBonusStuff(); } foobar(); } 

Appelez le via:

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

Le gabarit “laid” peut être évité en ajoutant de jolis wrappers aux fonctions:

 void MyFunctionAlone() { MyFunction(); } void MyFunctionBonus() { MyFunction(); } 

Vous pouvez trouver quelques informations intéressantes sur cette technique. C’est un “vieux” papier, mais la technique en soi rest totalement correcte.

Si vous avez access à un joli compilateur C ++ 17, vous pouvez même pousser plus loin la technique en utilisant constexpr si , comme ça:

 template  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 comstack // if a bonus different than 0,1,2,3 is passer else { static_assert(false);}, foorbar(); } 

Compte tenu de certains commentaires que l’OP a faits concernant le débogage, voici une version qui appelle doBonusStuff() pour les versions de débogage, mais pas pour les versions (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 la macro assert si vous souhaitez vérifier une condition et échouer si elle est fausse (mais uniquement pour les versions de débogage; les versions de compilation n’effectueront pas la vérification).

Faites attention si doBonusStuff() a des effets secondaires, car ces effets secondaires ne seront pas présents dans les versions validées et peuvent invalider les hypothèses faites dans le code.

Voici une légère variation de la réponse de Jarod42 en utilisant des modèles variadiques pour que l’appelant puisse fournir zéro ou une fonction supplémentaire:

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

Code d’appel:

 MyFunction(); MyFunction(&doBonusStuff); 

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

 #include  using namespace std; void foo() { cout << "foo\n"; }; void bar() { cout << "bar\n"; }; void bak() { cout << "bak\n"; }; template  void bonus() {}; template <> void bonus() { cout << "Doing bonus\n"; }; template  void MyFunc() { foo(); bar(); bonus(); bak(); } int main(int argc, const char* argv[]) { MyFunc(); cout << "\n"; MyFunc(); } output: foo bar bak foo bar Doing bonus bak 

Il n’y a plus qu’une seule version de MyFunc() avec le paramètre bool comme argument de template.

Vous pouvez utiliser la répartition des balises et la surcharge des fonctions simples:

 struct Tag_EnableBonus {}; struct Tag_DisableBonus {}; void doBonusStuff(Tag_DisableBonus) {} void doBonusStuff(Tag_EnableBonus) { //Do bonus stuff here } template MyFunction(Tag bonus_tag) { foo(); bar(); doBonusStuff(bonus_tag); foobar(); } 

Ceci est facile à lire / à comprendre, peut être étendu sans transpiration (et sans clauses standard – en ajoutant des balises), et ne laissera bien sûr aucune empreinte à l’exécution.

La syntaxe d’appel est plutôt conviviale, mais peut bien sûr être intégrée à des appels:

 void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); } void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); } 

Tag dispatching est une technique de programmation générique très utilisée, voici un article intéressant sur les bases.