Accertarsi in fase di compilazione che un metodo venga chiamato esattamente in una posizione


15

Sono curioso di sapere se è possibile garantire in fase di compilazione che un metodo venga chiamato esattamente in un unico posto.

Si noti che è OK se la funzione viene chiamata più di una volta (ad esempio in un ciclo), ma non dovrebbe essere chiamata in due cicli separati.

Questo può essere suddiviso in due parti, sono anche interessato a soluzioni che coprono entrambe le parti:
(a) assicurarsi che un metodo sia chiamato in almeno un posto
(b) assicurarsi che un metodo sia chiamato al massimo in un posto

Ho il pieno controllo della struttura del codice e sono benvenuti diversi modi di dire che ottengono la stessa idea.

// class.h

class MyClass {
  public:
    void my_method();
}

Non compilare (mai chiamato) quanto segue

#include "class.h"

int main() {
  MyClass my_class;
}

Non compilare quanto segue (chiamato in più di un posto)

#include "class.h"

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

Il seguente dovrebbe compilare (chiamato esattamente in un posto):

#include "class.h"

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

2
Non renderlo un metodo. Metti il ​​codice in linea in quel punto.
user207421

2
Penso che potresti farlo anche con un lambda (potrebbe essere un lambda vuoto) perché il tipo di chiusura è unico per ogni lambda. Ancora una volta, questo sarebbe un errore di runtime ma non è quello che hai chiesto. Se fornisci maggiori dettagli sul problema che stai cercando di risolvere, potremmo essere in grado di trovare un modo per aggirare questo.
Indiana Kernick il

2
A tale scopo, è possibile utilizzare la __COUNTER__macro non standard . Qualcosa del genere static_assert(__COUNTER__ == 0); my_class.my_method();. Tuttavia, il contatore si reimposta in ciascuna unità di traduzione in modo da poter verificare che la funzione venga chiamata una sola volta per unità di traduzione.
Indiana Kernick il

4
Perché vuoi farlo? Parte del punto di una funzione è che può essere chiamata da più posizioni.
Chipster

4
Dovresti spiegare perché vuoi farlo. Forse la soluzione che stai chiedendo non è la migliore per raggiungere i tuoi obiettivi reali.
tenfour

Risposte:


6

Approccio a bassa tecnologia:

Dato che hai il controllo sulla struttura del codice (che include il sistema di compilazione, suppongo), ecco una soluzione a bassa tecnologia:

  • rendere il nome della funzione sufficientemente unico
  • grep per il nome della funzione nel tuo codice. Lo aspetti due volte (supponendo che la tua dichiarazione e definizione siano colocate):
    • Una volta nell'intestazione
    • Una volta nel sito di chiamata singola

In alternativa:

Se davvero, davvero, vuoi davvero risolverlo con C ++, allora potresti provare

  • Usa un contatore del tempo di compilazione per capire il numero di usi all'interno di una unità di compilazione
  • Assicurarsi che la funzione violerebbe ODR se l'intestazione è inclusa in più unità di compilazione.

Tuttavia, i contatori dei tempi di compilazione sono magia nera (dice io, e mi piace molto il TMP), e forzare violazioni ODR a questo scopo sembra un voodoo simile (almeno avresti bisogno di un caso di prova che non riesce a collegarsi).

Ma sul serio:

Non farlo Qualunque cosa tu faccia, può essere pervertita quasi senza sforzo da una funzione wrapper:

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

MyClass::my_method()viene chiamato solo nel wrapper. Tutti gli altri chiamano semplicemente il wrapper, che probabilmente è persino inserito nel compilatore.

Come altri hanno suggerito: potrebbe essere molto più utile se spiegassi cosa stai cercando di fare.


1

Ecco un'idea approssimativa che potrebbe funzionare (troppo tempo per un commento, ma incompleta per una buona risposta SO).

È possibile ottenere questo risultato contando / controllando le istanze del modello.
I modelli vengono istanziati solo dopo l' uso .

Allo stesso modo, i corpi del metodo / funzione del modello non vengono analizzati, né compilati né collegati (oltre a garantire una sintassi valida) se non vengono mai chiamati. Ciò significa che non vengono effettuate istanze all'interno dei loro corpi).

Potresti essere in grado di creare un modello che mantenga un certo numero di istanze globali e asserzioni statiche su questo (o qualche altro meccanismo TMP per controllare le istanze passate).


Il conteggio delle istanze "globale" sarebbe locale all'unità di compilazione corrente.
atomsymbol

1

Esiste una soluzione parziale a questa domanda usando il preprocessore C e l'assemblaggio in linea GNU:

File di intestazione 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:");

File di implementazione 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;
}

Questa soluzione è parziale nel senso che non impedisce al programma di chiamare direttamente il metodo che inizia con il trattino basso senza usare la macro wrapper.


0

Usa un contatore constexpr. C'è un'implementazione in un'altra domanda


1
Sembra che questo metodo sia mal formato.
Chipster

Il problema open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 a cui fa riferimento una risposta a quella domanda afferma che si tratta di un bug dello standard e dovrebbe essere reso mal formato.
SD57

Quindi non è mal formato, almeno non ancora?
Chipster

Se non è ancora mal formato, dovrebbe essere utilizzato da quante più persone possibile il più rapidamente possibile, in modo che dovrebbero supportare questo caso d'uso!
user1685095
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.