web-dev-qa-db-fra.com

Dupliquer le 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 travailler de deux manières différentes:

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

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

Et je voudrais ne pas dupliquer mon code car la fonction réelle est beaucoup plus longue. Le problème est que je ne dois en aucun cas ajouter du temps d'exécution au programme lorsque MyFunction est appelée à la place de MyFunctionWithABonus. C'est pourquoi je ne peux pas simplement 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 sans temps d'exécution supplémentaire et sans duplication du code.

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

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

80
plougue

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();
}
55
Jarod42

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

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

Appelez-le via:

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

Le modèle "moche" peut être évité en ajoutant des wrappers Nice aux fonctions:

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

Vous pouvez trouver quelques informations intéressantes sur cette technique . C'est un "vieux" papier, mais la technique en elle-même reste tout à fait correcte.

Si vous avez accès à un compilateur Nice C++ 17, vous pouvez même pousser plus avant la technique, en utilisant le constexpr if , comme ceci:

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();
}
129
Gibet

Étant donné certains commentaires formulés par l'OP concernant le débogage, voici une version qui appelle doBonusStuff() pour les versions de débogage, mais pas les versions publiées (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 version n'effectuent pas la vérification).

Faites attention si doBonusStuff() a des effets secondaires, ceux-ci ne seront pas présents dans les versions de version et pourraient invalider les hypothèses formulées dans le code.

27
Cornstalks

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);
18
Chris Drew

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’existe plus qu’une version de MyFunc() avec le paramètre bool en tant qu’argument de modèle.

11
Sebastian Stern

Vous pouvez utiliser la distribution de balises et une surcharge de fonctions simple:

struct Tag_EnableBonus {};
struct Tag_DisableBonus {};

void doBonusStuff(Tag_DisableBonus) {}

void doBonusStuff(Tag_EnableBonus)
{
    //Do bonus stuff here
}

template<class Tag> MyFunction(Tag bonus_tag)
{
   foo();
   bar();
   doBonusStuff(bonus_tag);
   foobar();
}

Ceci est facile à lire/à comprendre, peut être étendu sans sueur (et pas de clauses sur les clauses habituelles if - en ajoutant plus de balises), et bien sûr ne laissera aucune empreinte au moment de l'exécution.

La syntaxe d’appel est assez conviviale, mais elle peut bien sûr être encapsulée dans des appels à la vanille:

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

La répartition des tags est une technique de programmation générique largement utilisée, here est un article de Nice sur les bases.

8
Ap31