Question

Si j'ai une fonction A(), je souhaite trouver une méthode pratique pour créer une fonction B() qui a exactement les mêmes fonctionnalités que A(), ne différant que par le nom. La nouvelle fonction serait à usage unique. L'intention est de différencier les appels à la même fonction dans un profileur d'échantillonnage quelque peu primitif, et la fonction dupliquée ne serait utilisée que dans ce contexte. Autrement dit, il ne toucherait jamais au code de production et ne serait utilisé que pour le bricolage.

La première hypothèse serait une macro qui déclare une fonction nommée B et crée un appel intégré à A() à l'intérieur de celle-ci. Le problème ici est que je ne connais pas une méthode dans GCC pour forcer un appel de fonction arbitraire à inline; il semble que toutes les options d'inlining concernent les déclarations de fonctions plutôt que les appels.

Il peut y avoir un moyen ésotérique de le faire avec des modèles, ou peut-être en incitant le compilateur à l'inlining. Je ne suis pas sûr que ce soit possible. Des pensées? Malheureusement, le nouveau standard C ++ n'est pas disponible, si cela ferait une différence.

Était-ce utile?

La solution

Utilisation de modèles

template<int x>
void A()
{
    // ..
}

int main()
{
    A<0>();
    A<1>();
    return 0;
}

<❯Mise à jour

Le compilateur peut être trop intelligent et ne créer qu'un seul corps pour A <0> et A <1>.Au moins Visual C ++ 2010 le fait en mode Release.Pour éviter cela, utilisez simplement le paramètre de modèle dans le corps du modèle de fonction dans les journaux ou les assertions.Par exemple,

#include <iostream>

template<int x>
void A()
{
    ::std::cout << x << std::endl;
    // ..
}

int main()
{
    A<0>();
    A<1>();
    auto v0 = A<0>;
    auto v1 = A<1>;
    ::std::cout << v0 << std::endl;
    ::std::cout << v1 << std::endl;
    ::std::cout << (v0 == v1) << std::endl;
    return 0;
}

Autres conseils

Cela fonctionne avec des modèles:

#include <iostream>                                                             

template<typename T>
void foo() {
    static int x = 0;
    std::cout << &x << std::endl;
}

int main(int argc, char **argv) {
    foo<int>();
    foo<float>();
    return 0;
}

Si vous exécutez cela, vous verrez deux valeurs différentes imprimées, reflétant le code généré par le compilateur pour les deux appels, même si le paramètre de modèle n'est pas utilisé.nm sur le fichier objet le confirme.

S'il s'agit d'un hack de débogage ponctuel, alors pourquoi pas:

#define A_CONTENT \
    ... // whatever

void A()
{
    A_CONTENT
}

void B()
{
    A_CONTENT
}

...

A();  // Call to A
B();  // Call to B  

Les macros sont généralement sinistres, mais nous ne parlons pas de code de production ici, alors qu'importe?

Ayant moi-même été dans cette voie, la réponse courte est que même si le compilateur émet deux doublons identiques d'une fonction, l'éditeur de liens d'optimisation remarquera qu'ils sont identiques et repliez-les ensemble en une seule implémentation.(Et si vous avez désactivé l'optimisation dans l'éditeur de liens, votre profil n'est plus valide à tout moment).

Dans le contexte d'un profileur d'échantillonnage, j'ai trouvé que l'approche la plus simple consiste à créer deux petits wrappers pour la fonction à la place:

void Func() { .... }

_declspec(noinline) 
void A_Func( return Func(); }
void B_Func( return Func(); }
void C_Func( return Func(); }

Ensuite, lorsque votre profileur échantillonnera la pile d'appels, vous serez en mesure de différencier les différents sites d'appel de cette fonction de manière très simple.

Vous pouvez toujours définir une macro, par exemple dans Chromium nous procédons comme suit pour réutiliser le code:

#define CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, SENDER, ARG1)     \
  static RETURN METHOD ## Thunk(SENDER sender, ARG1 one,            \
                                gpointer userdata) {                \
    return reinterpret_cast<CLASS*>(userdata)->METHOD(sender, one); \
  }                                                                 \
                                                                    \
  virtual RETURN METHOD(SENDER, ARG1);

Et nous les appelons comme:

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnExposeEvent, GdkEventExpose*);

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnButtonPressed, GdkEventButton*);

Vous pouvez faire quelque chose de similaire pour faire ce que vous vouliez.L'exemple ci-dessus nous montre en utilisant deux implémentations différentes mais avec une base de code commune.Pour les rappels GTK.

Ce que vous essayez vraiment de faire n'est pas clair, mais une solution vraiment laide serait de déclarer le corps de A comme une macro et ensuite vous pouvez "insérer" cette macro dans les fonctions que vous voulez.

De plus, les macros sont mauvaises.Ne les utilisez jamais à moins que vous ne soyez vraiment obligé de le faire.

Pourquoi vous souciez-vous tant de l'intégrer?Si vous créez une fonction wrapper, il y a de bonnes chances que le compilateur la mettra en ligne de toute façon.À tout le moins, il est peu probable que vous obteniez un cadre de fonction construit.

C ++ 11 vous permet également de faire ceci:

void A() {
    ...
}

...

auto B = [] () -> void { A(); };

Vous pouvez maintenant utiliser B syntaxiquement comme s'il s'agissait d'une fonction encapsulant A.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top