web-dev-qa-db-fra.com

Comment vérifier si la fonction existe en C/C++

Certaines situations dans mon code, je finis par invoquer la fonction uniquement si cette fonction est définie, sinon je ne devrais pas. Comment puis-je atteindre cet objectif ?

like:
if (function 'sum' exists ) then invoke sum ()

L’inverse pourrait peut-être poser la question suivante: Comment déterminer si une fonction est définie au moment de l’exécution et, dans l’affirmative, invoquer. 

19
Whoami

Tandis que les autres réponses sont des conseils utiles (dlsym, pointeurs de fonction, ...), vous ne pouvez pas compiler le code C++ faisant référence à une fonction qui n'existe pas. Au minimum, la fonction doit être déclarée _; sinon, votre code ne sera pas compilé. Si rien (une unité de compilation, un fichier objet, une bibliothèque) définit la fonction, l'éditeur de liens se plaindrait (à moins que ce soit faible, voir ci-dessous).

Mais vous devriez vraiment expliquer pourquoi vous demandez cela. Je ne peux pas deviner, et il existe un moyen d'atteindre votre objectif non déclaré.

Notez que dlsym requiert souvent des fonctions sans nom mangling , c'est-à-dire déclaré en tant que extern "C".

Si vous codez sous Linux avec GCC, vous pouvez également utiliser les déclarations weakfunction attribut in. L'éditeur de liens définirait ensuite les symboles faibles non définis sur null.

addenda

Si vous obtenez le nom de la fonction à partir d’une entrée, vous devez savoir que seul un sous-ensemble de fonctions doit être appelé de cette façon (si vous appelez une fonction arbitraire sans précaution, cela plantera!) Et vous ferez mieux de construire explicitement ce sous-ensemble. . Vous pouvez ensuite utiliser un std::map ou dlsym (avec chaque fonction du sous-ensemble déclaré extern "C"). Notez que dlopen avec un chemin NULL donne un descripteur au programme principal, que vous devez lier avec -rdynamic pour le faire fonctionner correctement.

Vous voulez vraiment appeler par leur nom uniquement un sous-ensemble de fonctions défini de manière appropriée. Par exemple, vous ne voulez probablement pas appeler ainsi abort, exit ou fork.

NB Si vous connaissez dynamiquement la signature de la fonction appelée, vous pouvez utiliser libffi pour l'appeler.

9

Lorsque vous déclarez 'somme', vous pouvez le déclarer comme:

#define SUM_EXISTS
int sum(std::vector<int>& addMeUp) {
    ...
}

Ensuite, quand vous venez l’utiliser, vous pouvez aller:

#ifdef SUM_EXISTS
int result = sum(x);
...
#endif

Je suppose que vous venez d'un langage de script où tout est fait au moment de l'exécution. La principale chose à retenir avec C++ est les deux phases:

  • Temps de compilation
    • Le préprocesseur s'exécute
    • le code de modèle est transformé en code source réel
    • le code source est activé en code machine
  • runtime
    • le code machine est exécuté

Donc, tous les #define et des choses comme ça se produisent au moment de la compilation.

....

Si vous vouliez vraiment tout faire au moment de l'exécution ... vous pourriez être intéressé par certains des produits d'architecture composant disponibles sur le marché.

Ou peut-être que vous recherchez une architecture de type plugin .

12
matiu

utiliser des pointeurs vers des fonctions.

 //initialize
 typedef void (*PF)();
 std::map<std::string, PF> defined_functions;
 defined_functions["foo"]=&foo;
 defined_functions["bar"]=&bar;
 //if defined, invoke it
 if(defined_functions.find("foo") != defined_functions.end())
 {
     defined_functions["foo"]();
 }
10
BruceAdi

Je soupçonne que l’affiche cherchait quelque chose de plus dans le sens du contrôle/envoi de SFINAE. Avec les modèles C++, vous pouvez définir des fonctions de modèle, l'une qui appelle la fonction souhaitée (si elle existe) et l'autre qui ne fait rien (si la fonction n'existe pas). Vous pouvez ensuite faire en sorte que le premier modèle dépende de la fonction souhaitée, de sorte que le modèle soit mal formé lorsque la fonction n'existe pas. Ceci est valide car dans C++, l'échec de la substitution de modèle n'est pas une erreur (SFINAE), aussi le compilateur se contentera-t-il de revenir au second cas (ce qui par exemple ne pourrait rien faire).

Voir ici pour un excellent exemple: Est-il possible d'écrire un modèle pour vérifier l'existence d'une fonction?

9
fredbaba

En utilisant GCC, vous pouvez:

void func(int argc, char *argv[]) __attribute__((weak)); // weak declaration must always be present

// optional definition:
/*void func(int argc, char *argv[]) { 
    printf("ENCONTRE LA FUNC\n");
    for(int aa = 0; aa < argc; aa++){
        printf("arg %d = %s \n", aa, argv[aa]);
    }
}*/

int main(int argc, char *argv[]) {
    if (func){ 
        func(argc, argv); 
    } else {
        printf("no encontre la func\n");
    }
}

Si vous ne commentez pas func, il s'exécutera sinon le message "no encontre la func\n" sera affiché.

Si vous savez dans quelle bibliothèque se trouve la fonction que vous souhaitez appeler, vous pouvez utiliser dlsym() et dlerror() pour savoir si elle est présente ou non, ainsi que le pointeur de la fonction.

Edit: Je n'utiliserais probablement pas cette approche. Je recommanderais plutôt la solution de Matiu, car je pense que c'est une bien meilleure pratique. Cependant, dlsym() n'est pas très connu, alors j'ai pensé le préciser.

4
Timothy Jones

Donc, une autre façon, si vous utilisez c ++ 11 serait d’utiliser des foncteurs:

Vous aurez besoin de mettre ceci au début de votre fichier:

#include <functional>

Le type d'un foncteur est déclaré dans ce format:

std::function< return_type (param1_type, param2_type) >

Vous pouvez ajouter une variable contenant un foncteur pour somme comme ceci:

std::function<int(const std::vector<int>&)> sum;

Pour rendre les choses faciles, laissez raccourcir le type param:

using Numbers = const std::vectorn<int>&;

Ensuite, vous pouvez renseigner la variable foncteur avec l’un des éléments suivants:

Un lambda:

sum = [](Numbers x) { return std::accumulate(x.cbegin(), x.cend(), 0); } // std::accumulate comes from #include <numeric>

Un pointeur de fonction:

int myFunc(Numbers nums) {
    int result = 0;
    for (int i : nums)
        result += i;
    return result;
}
sum = &myFunc;

Quelque chose qui "lie" a créé:

struct Adder {
    int startNumber = 6;
    int doAdding(Numbers nums) {
        int result = 0;
        for (int i : nums)
            result += i;
        return result;
    }
};
...
Adder myAdder{2}; // Make an adder that starts at two
sum = std::bind(&Adder::doAdding, myAdder);

Enfin, pour l’utiliser, c’est une simple déclaration if:

if (sum)
    return sum(x);

En résumé, les foncteurs sont le nouveau pointeur sur une fonction, mais ils sont plus polyvalents. Peut en fait être en ligne si le compilateur est assez sûr, mais est généralement identique à un pointeur de fonction.

Combinés à std :: bind et à lambda, ils sont tout à fait supérieurs aux anciens pointeurs de fonction de style C.

Mais rappelez-vous qu'ils travaillent dans les environnements c ++ 11 et supérieurs. (Pas en C ou C++ 03).

2
matiu

Vous pouvez utiliser #pragma weak pour les compilateurs qui le supportent (voir l’entrée symbole faible wikipedia).

Cet exemple et ce commentaire proviennent de L'histoire de l'intérieur sur les bibliothèques partagées et le chargement dynamique :

#pragma weak debug
extern void debug(void);
void (*debugfunc)(void) = debug;
int main() {
    printf(“Hello World\n”);
    if (debugfunc) (*debugfunc)();
}

vous pouvez utiliser le pragma faible pour forcer l'éditeur de liens à ignorer les fichiers non résolus symboles [..] le programme compile et lie si debug () .__ ou non. est en réalité défini dans n'importe quel fichier objet. Quand le symbole reste indéfini, l’éditeur de liens remplace généralement sa valeur par 0. Donc, ceci technique peut être un moyen utile pour un programme d’appeler du code facultatif cela ne nécessite pas de recompiler toute l'application.

1
mihai