Upewnij się w czasie kompilacji, że metoda jest wywoływana dokładnie w jednym miejscu

15

Zastanawiam się, czy w czasie kompilacji można upewnić się, że metoda jest wywoływana dokładnie w jednym miejscu.

Zauważ, że jest OK, jeśli funkcja jest wywoływana więcej niż raz (np. W pętli) - ale nie powinna być wywoływana w dwóch oddzielnych pętlach.

Można to podzielić na dwie części, interesują mnie również rozwiązania obejmujące każdą część:
(a) upewnij się, że metoda jest wywoływana w co najmniej jednym miejscu
(b) upewnij się, że metoda jest wywoływana w co najwyżej jednym miejscu

Mam pełną kontrolę nad strukturą kodu i mile widziane są różne idiomy, które osiągają ten sam pomysł.

// class.h

class MyClass {
  public:
    void my_method();
}

Poniższe nie powinny się kompilować (nigdy nie są wywoływane)

#include "class.h"

int main() {
  MyClass my_class;
}

Poniższe nie powinny się kompilować (wywoływane w więcej niż jednym miejscu)

#include "class.h"

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

Powinny się kompilować (wywoływane dokładnie w jednym miejscu):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
royoy
źródło
2
Nie rób z tego metody. Umieść kod w jednym miejscu.
user207421
2
Myślę, że można to również zrobić z lambda (może to być pusta lambda), ponieważ rodzaj zamknięcia jest unikalny dla każdej lambda. Znowu byłby to błąd czasu wykonywania, ale nie o to prosiłeś. Jeśli podasz więcej szczegółów na temat problemu, który próbujesz rozwiązać, być może uda nam się to rozwiązać.
Indiana Kernick
2
W tym celu możesz użyć niestandardowego __COUNTER__makra. Coś jak static_assert(__COUNTER__ == 0); my_class.my_method();. Jednak licznik resetuje się w każdej jednostce tłumaczeniowej, dzięki czemu można sprawdzić, czy funkcja jest wywoływana tylko raz na jednostkę tłumaczeniową.
Indiana Kernick
4
Dlaczego chcesz to zrobić? Część funkcji polega na tym, że można ją wywoływać z wielu miejsc.
Chipster
4
Powinieneś wyjaśnić, dlaczego chcesz to zrobić. Być może rozwiązanie, o które prosisz, nie jest najlepszym sposobem na osiągnięcie twoich prawdziwych celów.
tenfour

Odpowiedzi:

6

Podejście Low Tech:

Ponieważ masz kontrolę nad strukturą kodu (która obejmuje system kompilacji, jak zakładam), oto rozwiązanie mało zaawansowane:

  • uczynić nazwę funkcji wystarczająco unikalną
  • grep dla nazwy funkcji w kodzie. Oczekujesz tego dwukrotnie (zakładając, że deklaracja i definicja są kolokowane):
    • Raz w nagłówku
    • Raz na stronie pojedynczego połączenia

Alternatywnie:

Jeśli naprawdę, naprawdę, naprawdę chcesz to rozwiązać za pomocą C ++, możesz spróbować

  • Użyj licznika czasu kompilacji, aby ustalić liczbę zastosowań w jednostkach kompilacji
  • Upewnij się, że funkcja naruszy ODR, jeśli nagłówek zostanie zawarty w wielu jednostkach kompilacyjnych.

Jednak liczniki czasu kompilacji to czarna magia (mówi ja i ja naprawdę lubię TMP), a wymuszanie naruszeń ODR w tym celu wydaje się podobne do voodoo (przynajmniej wymagałbyś testowego przypadku, który nie łączy się).

Ale poważnie:

Nie rób tego Cokolwiek zrobisz, funkcja owijania może zostać wypaczona prawie bez wysiłku:

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

MyClass::my_method()jest wywoływany tylko w opakowaniu. Wszyscy inni nazywają otoki, które prawdopodobnie są nawet kompilowane przez kompilator.

Jak sugerowali inni: może być o wiele bardziej pomocne, jeśli wyjaśnisz, co próbujesz zrobić.

Rumburak
źródło
1

Oto szorstki pomysł, który może zadziałać (za długi na komentarz - ale niekompletny na dobrą odpowiedź SO).

Możesz to osiągnąć, licząc / sprawdzając instancje szablonów.
Szablony tworzone są tylko po użyciu .

Podobnie ciała metod / funkcji szablonów nie są analizowane, kompilowane ani łączone (poza zapewnieniem poprawnej składni), jeśli nigdy nie zostaną wywołane. Oznacza to, że wszelkie instancje w ich ciałach nie są dokonywane).

Możesz być w stanie utworzyć szablon, który będzie utrzymywał pewną globalną liczbę instancji i statyczne potwierdzanie tego (lub inny mechanizm TMP do sprawdzania poprzednich instancji).

Adi Shavit
źródło
„Globalna” liczba instancji będzie lokalna dla bieżącej jednostki kompilacji.
atomsymbol
1

Istnieje częściowe rozwiązanie tego problemu przy użyciu preprocesora C i wbudowanego zestawu GNU:

Plik nagłówka 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:");

Plik implementacyjny 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;
}

To rozwiązanie jest częściowe w tym sensie, że nie uniemożliwia programowi wywołania metody rozpoczynającej się bezpośrednio od znaku podkreślenia bez użycia makra opakowania.

symbol atomowy
źródło
0

Użyj licznika constexpr. Istnieje implementacja w innym pytaniu

SD57
źródło
1
Wygląda na to, że ta metoda jest źle sformułowana.
Chipster
Problem open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 przywołany w odpowiedzi na to pytanie stwierdza, że ​​jest to błąd standardu i należy go źle sformułować.
SD57
Więc to nie jest źle sformułowane, a przynajmniej jeszcze nie?
Chipster
Jeśli nie jest jeszcze źle sformułowany, powinien jak najszybciej wykorzystać go jak najwięcej ludzi, aby musieli wesprzeć ten przypadek użycia!
user1685095