Garanta, em tempo de compilação, que um método seja chamado exatamente em um local

15

Estou curioso para saber se é possível garantir, em tempo de compilação, que um método seja chamado em exatamente um local.

Observe que não há problema se a função for chamada mais de uma vez (por exemplo, em um loop) - mas não deve ser chamada em dois loops separados.

Isso pode ser dividido em duas partes. Também estou interessado em soluções que abrangem qualquer parte:
(a) garantir que um método seja chamado em pelo menos um local
(b) garantir que um método seja chamado em mais de um local

Eu tenho controle total sobre a estrutura do código, e diferentes idiomas que alcançam a mesma idéia são bem-vindos.

// class.h

class MyClass {
  public:
    void my_method();
}

O seguinte não deve ser compilado (nunca chamado)

#include "class.h"

int main() {
  MyClass my_class;
}

O seguinte não deve ser compilado (chamado em mais de um local)

#include "class.h"

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

O seguinte deve ser compilado (chamado exatamente em um local):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
yoyoy
fonte
2
Não faça disso um método. Coloque o código embutido nesse local.
user207421 27/03
2
Eu acho que você também pode fazer isso com um lambda (poderia ser um lambda vazio) porque o tipo de fechamento é único para cada lambda. Novamente, isso seria um erro de tempo de execução, mas não foi o que você pediu. Se você fornecer mais detalhes sobre o problema que está tentando resolver, talvez seja possível encontrar uma maneira de contornar isso.
Indiana Kernick 27/03
2
Você pode usar a __COUNTER__macro não padrão para fazer isso. Algo como static_assert(__COUNTER__ == 0); my_class.my_method();. No entanto, o contador é redefinido em cada unidade de tradução, portanto você só pode verificar se a função é chamada uma vez por unidade de tradução.
Indiana Kernick 27/03
4
Por que você quer fazer isso? Parte do objetivo de uma função é que ela pode ser chamada de vários locais.
Chipster 27/03
4
Você deve explicar por que deseja fazer isso. Talvez a solução que você está pedindo não seja a melhor para atingir seus objetivos reais.
tenfour 30/03

Respostas:

6

Abordagem de baixa tecnologia:

Como você tem controle sobre a estrutura de código (que inclui o sistema de compilação, suponho), aqui está uma solução de baixa tecnologia:

  • torne o nome da função suficientemente único
  • grep para o nome da função no seu código. Você está esperando duas vezes (supondo que sua declaração e definição estejam colocadas):
    • Uma vez no cabeçalho
    • Uma vez no site de chamada única

Alternativamente:

Se você realmente, realmente, realmente quer resolvê-lo com C ++, pode tentar

  • Use um contador de tempo de compilação para descobrir o número de usos em unidades de compilação
  • Verifique se a função violaria o ODR se o cabeçalho estiver incluído em várias unidades de compilação.

No entanto, os contadores de tempo de compilação são magia negra (diz eu e gosto muito de TMP), e forçar violações de ODR para esse fim parece um vodu semelhante (pelo menos você exigiria um caso de teste que não vincule).

Mas seriamente:

Não faça isso. Faça o que fizer, ele pode ser pervertido sem quase nenhum esforço por uma função de wrapper:

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

MyClass::my_method()é chamado apenas no wrapper. Todos os demais chamam o wrapper, que provavelmente está embutido no compilador.

Como outros sugeriram: pode ser muito mais útil se você explicar o que está tentando fazer.

Rumburak
fonte
1

Aqui está uma idéia aproximada que pode funcionar (muito tempo para um comentário - mas incompleta para uma boa resposta SO).

Você pode conseguir isso contando / verificando as instâncias do modelo.
Os modelos são instanciados somente após o uso .

Da mesma forma, os corpos de método / função do modelo não são analisados, compilados ou vinculados (além de garantir a sintaxe válida) se nunca forem chamados. Isso significa que quaisquer instanciações dentro de seus corpos não são feitas).

Você pode criar um modelo que mantenha alguma contagem de instanciação global e afirmação estática sobre isso (ou algum outro mecanismo TMP para verificar instanciações passadas).

Adi Shavit
fonte
A contagem de instanciação "global" seria local para a unidade de compilação atual.
atomsymbol 17/04
1

Há uma solução parcial para essa pergunta usando o pré-processador C e o assembly embutido GNU:

Arquivo de cabeçalho 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:");

Arquivo de implementação 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;
}

Essa solução é parcial no sentido de que não impede que o programa chame o método começando diretamente com o sublinhado sem usar a macro do wrapper.

atomsymbol
fonte
0

Use um contador constexpr. Existe uma implementação em outra pergunta

SD57
fonte
11
Parece que esse método está mal formado.
Chipster 31/03
O problema open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 referenciado em uma resposta a essa pergunta afirma que é um bug do padrão e deve ser mal formado.
SD57 31/03
Então não está mal formado, pelo menos ainda não?
Chipster 31/03
Se ainda não estiver mal formado, deve ser usado pelo maior número possível de pessoas o mais rápido possível, para que eles tenham que suportar esse caso de uso!
User1685095 31/03