Stellen Sie beim Kompilieren sicher, dass eine Methode an genau einer Stelle aufgerufen wird

15

Ich bin gespannt, ob es beim Kompilieren möglich ist, sicherzustellen, dass eine Methode an genau einer Stelle aufgerufen wird.

Beachten Sie, dass es in Ordnung ist, wenn die Funktion mehrmals aufgerufen wird (z. B. in einer Schleife). Sie sollte jedoch nicht in zwei separaten Schleifen aufgerufen werden.

Dies kann in zwei Teile unterteilt werden. Ich bin auch an Lösungen interessiert, die beide Teile abdecken:
(a) Sicherstellen, dass eine Methode an mindestens einer Stelle aufgerufen wird.
(B) Sicherstellen, dass eine Methode an höchstens einer Stelle aufgerufen wird

Ich habe die volle Kontrolle über die Struktur des Codes, und verschiedene Redewendungen, die dieselbe Idee erreichen, sind willkommen.

// class.h

class MyClass {
  public:
    void my_method();
}

Folgendes sollte nicht kompiliert werden (nie aufgerufen)

#include "class.h"

int main() {
  MyClass my_class;
}

Folgendes sollte nicht kompiliert werden (an mehr als einer Stelle aufgerufen)

#include "class.h"

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

Folgendes sollte kompiliert werden (an genau einer Stelle aufgerufen):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
yoyoy
quelle
2
Mach es nicht zu einer Methode. Setzen Sie den Code an dieser einen Stelle ein.
user207421
2
Ich denke, Sie könnten dies auch mit einem Lambda tun (es könnte ein leeres Lambda sein), da die Art eines Verschlusses für jedes Lambda einzigartig ist. Auch dies wäre ein Laufzeitfehler, aber das haben Sie nicht verlangt. Wenn Sie weitere Details zu dem Problem angeben, das Sie lösen möchten, können wir möglicherweise einen Weg finden, dies zu umgehen.
Indiana Kernick
2
Sie können dazu das nicht standardmäßige __COUNTER__Makro verwenden. So etwas wie static_assert(__COUNTER__ == 0); my_class.my_method();. Der Zähler wird jedoch in jeder Übersetzungseinheit zurückgesetzt, sodass Sie nur einmal überprüfen können, ob die Funktion pro Übersetzungseinheit aufgerufen wird.
Indiana Kernick
4
Warum willst du das machen? Ein Teil des Punktes einer Funktion ist, dass sie von mehreren Stellen aus aufgerufen werden kann.
Chipster
4
Sie sollten erklären, warum Sie dies tun möchten. Vielleicht ist die Lösung, nach der Sie fragen, nicht die beste, um Ihre wirklichen Ziele zu erreichen.
30.

Antworten:

6

Low-Tech-Ansatz:

Da Sie die Kontrolle über die Codestruktur haben (einschließlich des Build-Systems, nehme ich an), ist hier eine Low-Tech-Lösung:

  • Machen Sie den Funktionsnamen ausreichend eindeutig
  • grep für den Funktionsnamen in Ihrem Code. Sie erwarten es zweimal (vorausgesetzt, Ihre Deklaration und Definition sind kolokalisiert):
    • Einmal in der Kopfzeile
    • Einmal an der Single Call Site

Alternative:

Wenn Sie es wirklich, wirklich, wirklich mit C ++ lösen möchten, können Sie es versuchen

  • Verwenden Sie einen Kompilierungszeitzähler, um die Anzahl der Verwendungen innerhalb einer Kompilierungseinheit zu ermitteln
  • Stellen Sie sicher, dass die Funktion die ODR verletzt, wenn der Header in mehreren Kompilierungseinheiten enthalten ist.

Kompilierungszeitzähler sind jedoch schwarze Magie (sagt ich, und ich mag TMP wirklich), und das Erzwingen von ODR-Verstößen zu diesem Zweck scheint ein ähnliches Voodoo zu sein (zumindest würden Sie einen Testfall benötigen, der nicht verknüpft werden kann).

Aber ernsthaft:

Tu das nicht. Was auch immer Sie tun, es kann fast ohne Aufwand durch eine Wrapper-Funktion pervertiert werden:

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

MyClass::my_method()wird nur im Wrapper aufgerufen. Alle anderen rufen nur den Wrapper auf, der wahrscheinlich sogar vom Compiler eingefügt wird.

Wie andere vorgeschlagen haben: Es könnte viel hilfreicher sein, wenn Sie erklären würden, was Sie versuchen zu tun.

Rumburak
quelle
1

Hier ist eine grobe Idee, die möglicherweise funktioniert (zu lang für einen Kommentar - aber unvollständig für eine gute SO-Antwort).

Möglicherweise können Sie dies erreichen, indem Sie Vorlageninstanziierungen zählen / überprüfen.
Vorlagen werden nur bei Verwendung instanziiert .

In ähnlicher Weise werden Vorlagenmethoden- / Funktionskörper weder analysiert noch kompiliert oder verknüpft (über die Gewährleistung einer gültigen Syntax hinaus), wenn sie niemals aufgerufen werden. Dies bedeutet, dass keine Instanziierungen innerhalb ihres Körpers vorgenommen werden.

Möglicherweise können Sie eine Vorlage erstellen, die eine globale Anzahl von Instanziierungen und statische Zusicherungen beibehält (oder einen anderen TMP-Mechanismus, um frühere Instanziierungen zu überprüfen).

Adi Shavit
quelle
Die "globale" Instanziierungsanzahl wäre lokal für die aktuelle Kompilierungseinheit.
Atomsymbol
1

Es gibt eine Teillösung für diese Frage unter Verwendung des C-Präprozessors und der GNU-Inline-Baugruppe:

Header-Datei 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:");

Implementierungsdatei 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;
}

Diese Lösung ist insofern teilweise, als sie das Programm nicht daran hindert, die mit dem Unterstrich beginnende Methode direkt aufzurufen, ohne das Wrapper-Makro zu verwenden.

Atomsymbol
quelle
0

Verwenden Sie einen Constexpr-Zähler. Es gibt eine Implementierung in einer anderen Frage

SD57
quelle
1
Es klingt so, als ob diese Methode schlecht geformt ist.
Chipster
Das Problem open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118, auf das in einer Antwort auf diese Frage verwiesen wird, besagt, dass es sich um einen Fehler des Standards handelt und schlecht formuliert werden sollte.
SD57
Also ist es nicht schlecht geformt, zumindest noch nicht?
Chipster
Wenn es noch nicht schlecht geformt ist, sollte es von so vielen Menschen wie möglich so schnell wie möglich verwendet werden, damit sie diesen Anwendungsfall unterstützen müssen!
user1685095