Assurez-vous au moment de la compilation qu'une méthode est appelée exactement à un endroit

15

Je suis curieux de savoir s'il est possible de garantir au moment de la compilation qu'une méthode est appelée exactement à un endroit.

Notez que c'est OK si la fonction est appelée plus d'une fois (par exemple dans une boucle) - mais elle ne doit pas être appelée dans deux boucles distinctes.

Cela peut être divisé en deux parties, je suis également intéressé par les solutions qui couvrent l'une ou l'autre partie:
(a) s'assurer qu'une méthode est appelée à au moins un endroit
(b) s'assurer qu'une méthode est appelée à au plus un endroit

J'ai un contrôle total sur la structure du code et les différents idiomes qui atteignent la même idée sont les bienvenus.

// class.h

class MyClass {
  public:
    void my_method();
}

Les éléments suivants ne doivent pas être compilés (jamais appelés)

#include "class.h"

int main() {
  MyClass my_class;
}

Les éléments suivants ne doivent pas être compilés (appelés à plusieurs endroits)

#include "class.h"

int main() {
  MyClass my_class;
  my_class.my_method();
  while(true) {
    my_class.my_method();
  }
}

Les éléments suivants doivent être compilés (appelés exactement à un endroit):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
yoyoy
la source
2
N'en faites pas une méthode. Mettez le code en ligne à cet endroit.
user207421
2
Je pense que vous pouvez également le faire avec un lambda (ce pourrait être un lambda vide) car le type de fermeture est unique pour chaque lambda. Encore une fois, ce serait une erreur d'exécution, mais ce n'est pas ce que vous avez demandé. Si vous fournissez plus de détails sur le problème que vous essayez de résoudre, nous pourrons peut-être trouver un moyen de contourner ce problème.
Indiana Kernick
2
Vous pouvez utiliser la __COUNTER__macro non standard pour ce faire. Quelque chose comme static_assert(__COUNTER__ == 0); my_class.my_method();. Cependant, le compteur est réinitialisé dans chaque unité de traduction, vous ne pouvez donc vérifier que la fonction est appelée une fois par unité de traduction.
Indiana Kernick
4
Pourquoi est ce que tu veux faire ça? Une partie du point d'une fonction est qu'elle peut être appelée à partir de plusieurs endroits.
Chipster
4
Vous devez expliquer pourquoi vous souhaitez procéder ainsi. Peut-être que la solution que vous demandez n'est pas la meilleure pour atteindre vos objectifs réels.
tenfour

Réponses:

6

Approche Low Tech:

Puisque vous contrôlez la structure du code (qui inclut le système de construction, je suppose), voici une solution à faible technologie:

  • rendre le nom de la fonction suffisamment unique
  • grep pour le nom de la fonction dans votre code. Vous l'attendez deux fois (en supposant que votre déclaration et votre définition sont colocalisées):
    • Une fois dans l'en-tête
    • Une fois sur le site d'appel unique

Alternativement:

Si vous voulez vraiment, vraiment, vraiment le résoudre avec C ++, alors vous pouvez essayer

  • Utilisez un compteur de temps de compilation pour déterminer le nombre d'utilisations dans les unités de compilation
  • Assurez-vous que la fonction violerait ODR si l'en-tête est inclus dans plusieurs unités de compilation.

Cependant, les compteurs de temps de compilation sont de la magie noire (dit moi et j'aime vraiment TMP), et forcer les violations ODR à cet effet semble être un vaudou similaire (au moins, vous auriez besoin d'un cas de test qui ne parvient pas à se lier).

Mais sérieusement:

Ne fais pas ça. Quoi que vous fassiez, il peut être perverti sans effort par une fonction wrapper:

auto call_my_method(MyClass& o)
{
   return o.my_method();
}

MyClass::my_method()est appelé uniquement dans le wrapper. Tout le monde appelle simplement le wrapper qui est probablement même inséré par le compilateur.

Comme d'autres l'ont suggéré: il pourrait être beaucoup plus utile d'expliquer ce que vous essayez de faire.

Rumburak
la source
1

Voici une idée approximative qui peut fonctionner (trop longue pour un commentaire - mais incomplète pour une bonne réponse SO).

Vous pourrez peut-être y parvenir en comptant / vérifiant les instanciations du modèle.
Les modèles sont instanciés uniquement lors de leur utilisation .

De même, les corps de méthode / fonction de modèle ne sont pas analysés ni compilés ou liés (au-delà de la garantie d'une syntaxe valide) s'ils ne sont jamais appelés. Cela signifie qu'aucune instanciation au sein de leur corps n'est effectuée).

Vous pourrez peut-être créer un modèle qui conserve un certain nombre d'instanciations globales et une assertion statique sur celui-ci (ou un autre mécanisme TMP pour vérifier les instanciations passées).

Adi Shavit
la source
Le compte d'instanciation "global" serait local à l'unité de compilation actuelle.
atomesymbol
1

Il existe une solution partielle à cette question en utilisant le préprocesseur C et l'assemblage en ligne GNU:

Fichier d'en-tête a.h:

struct A {
    // Do not call this method directly, use the macro below to call it
    int _method_vUcaJB5NKSD3upQ(int i, int j);
};

// Use inline assembly to ensure that this macro is used at most once
#define method_vUcaJB5NKSD3upQ(args...) \
    _method_vUcaJB5NKSD3upQ(args); \
    asm (".global once_vUcaJB5NKSD3upQ; once_vUcaJB5NKSD3upQ:");

Fichier d'implémentation a.cc:

#include <iostream>
#include "a.h"

int A::_method_vUcaJB5NKSD3upQ(int i, int j) { return i+j+5; }

// Ensure that the macro is used at least once
extern "C" const char once_vUcaJB5NKSD3upQ;
static const char get_vUcaJB5NKSD3upQ = once_vUcaJB5NKSD3upQ;

int main() {
    A a;
    for(int i=0; i<7; i++) {
        // Use a separate statement to call the method
        // (terminated by a semicolon, it cannot be a sub-expression)
        auto x = a.method_vUcaJB5NKSD3upQ(2, 3);
        std::cout << x << std::endl;
    }
    return 0;
}

Cette solution est partielle dans le sens où elle n'empêche pas le programme d'appeler directement la méthode commençant par le trait de soulignement sans utiliser la macro wrapper.

atomesymbole
la source
0

Utilisez un compteur constexpr. Il y a une implémentation dans une autre question

SD57
la source
1
Il semble que cette méthode soit mal formée.
Chipster
Le problème open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 mentionné dans une réponse à cette question indique qu'il s'agit d'un bogue de la norme et qu'il devrait être mal formé.
SD57
Ce n'est donc pas mal formé, du moins pas encore?
Chipster
S'il n'est pas encore mal formé, il devrait être utilisé par autant de personnes que possible le plus rapidement possible, afin qu'ils aient à supporter ce cas d'utilisation!
user1685095