Убедитесь, что во время компиляции метод вызывается ровно в одном месте

15

Мне любопытно, можно ли гарантировать, что во время компиляции метод вызывается ровно в одном месте.

Обратите внимание, что все в порядке, если функция вызывается более одного раза (например, в цикле), но ее не следует вызывать в двух отдельных циклах.

Это может быть разбито на две части, меня также интересуют решения, которые охватывают любую часть:
(а) убедиться, что метод вызывается хотя бы в одном месте
(б) убедиться, что метод вызывается не более чем в одном месте

У меня есть полный контроль над структурой кода, и приветствуются различные идиомы, которые достигают той же идеи.

// class.h

class MyClass {
  public:
    void my_method();
}

Следующее не должно компилироваться (никогда не вызывается)

#include "class.h"

int main() {
  MyClass my_class;
}

Следующее не должно компилироваться (вызывается более чем в одном месте)

#include "class.h"

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

Следующее должно скомпилироваться (вызывается ровно в одном месте):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
yoyoy
источник
2
Не делай это методом. Поместите код в одном месте.
user207421
2
Я думаю, что вы также можете сделать это с лямбдой (это может быть пустая лямбда), потому что тип замыкания уникален для каждой лямбды. Опять же, это будет ошибка во время выполнения, но это не то, что вы просили. Если вы предоставите более подробную информацию о проблеме, которую вы пытаетесь решить, мы могли бы найти способ обойти это.
Индиана Керник
2
Вы можете использовать нестандартный __COUNTER__макрос для этого. Нечто подобное static_assert(__COUNTER__ == 0); my_class.my_method();. Однако счетчик сбрасывается в каждой единице перевода, поэтому вы можете проверить, что функция вызывается только один раз для каждой единицы перевода.
Индиана Керник
4
Почему ты хочешь это сделать? Частью функции является то, что ее можно вызывать из нескольких мест.
Чипстер
4
Вы должны объяснить, почему вы хотите это сделать. Возможно, решение, о котором вы просите, не является лучшим для достижения ваших реальных целей.
10

Ответы:

6

Низкий технический подход:

Так как у вас есть контроль над структурой кода (которая включает в себя систему сборки, я полагаю), вот низкотехнологичное решение:

  • сделать имя функции достаточно уникальным
  • grep для имени функции в вашем коде. Вы ожидаете этого дважды (при условии, что ваше объявление и определение находятся в одном месте):
    • Один раз в шапке
    • Один раз на сайте одного звонка

В качестве альтернативы:

Если вы действительно очень хотите решить эту проблему с помощью C ++, тогда вы можете попробовать

  • Используйте счетчик времени компиляции, чтобы выяснить количество использований в единицах компиляции
  • Убедитесь, что функция будет нарушать ODR, если заголовок включен в несколько блоков компиляции.

Однако счетчики времени компиляции - чёрная магия (говорит я, и мне действительно нравится TMP), и принудительное использование ODR-нарушений для этой цели кажется похожим на voodoo (по крайней мере, вам потребуется тестовый пример, который не может соединиться).

Но серьезно:

Не делай этого. Что бы вы ни делали, это может быть практически без усилий извращено функцией-оберткой:

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

MyClass::my_method()вызывается только в обёртке. Все остальные просто вызывают обертку, которая, вероятно, даже встроена компилятором.

Как и предполагали другие: было бы гораздо полезнее, если бы вы объяснили, что вы пытаетесь сделать.

Rumburak
источник
1

Вот грубая идея, которая может сработать (слишком долго для комментария - но неполно для хорошего ответа SO).

Вы можете достичь этого, посчитав / проверив экземпляры шаблона.
Шаблоны создаются только при использовании .

Точно так же тела метода / функции шаблона не анализируются, не компилируются и не связываются (за исключением обеспечения допустимого синтаксиса), если они никогда не вызываются. Это означает, что никаких инстанциаций внутри их тел не производится).

Возможно, вам удастся создать шаблон, который будет поддерживать некоторое глобальное количество экземпляров и статическое утверждение для этого (или какой-то другой механизм TMP для проверки прошлых экземпляров).

Ади Шавит
источник
«Глобальный» счетчик экземпляров будет локальным по отношению к текущему модулю компиляции.
Атомсимвол
1

Существует частичное решение этого вопроса с использованием препроцессора C и встроенной сборки GNU:

Заголовочный файл 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:");

Файл реализации 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;
}

Это решение является частичным в том смысле, что оно не мешает программе вызывать метод, начинающийся непосредственно с подчеркивания, без использования макроса-оболочки.

atomsymbol
источник
0

Используйте счетчик constexpr. Есть реализация в другом вопросе

SD57
источник
1
Похоже, этот метод плохо сформирован.
Чипстер
В проблеме open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118, на которую есть ссылка в ответе на этот вопрос, говорится, что это ошибка стандарта и ее следует сделать некорректной.
SD57
Так это не плохо сформировано, по крайней мере, пока?
Чипстер
Если он еще не сформирован, его следует использовать как можно большему количеству людей, чтобы они могли поддержать этот вариант использования!
user1685095