Переконайтесь, що час компіляції викликається саме в одному місці


15

Мені цікаво, чи можна за час компіляції забезпечити виклик методу саме в одному місці.

Зауважте, що це нормально, якщо функція викликається декілька разів (наприклад, у циклі) - але вона не повинна викликатися у двох окремих циклах.

Це можна розбити на дві частини, мене також цікавлять рішення, які охоплюють будь-яку частину:
(a) забезпечення того, що метод викликається принаймні в одному місці
(b) забезпечення методу виклику щонайменше в одному місці

Я маю повний контроль над структурою коду, і різні ідіоми, які досягають тієї самої ідеї, вітаються.

// 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();
  }
}

2
Не робіть це методом. Покладіть код в рядку в одне місце.
— користувач207421

2
Я думаю, ви також могли це зробити з лямбда (це може бути порожня лямбда), оскільки тип закриття є унікальним для кожної лямбда. Знову ж таки, це буде помилка виконання, але це не те, про що ви просили. Якщо ви надасте детальну інформацію про проблему, яку ви намагаєтеся вирішити, ми можемо знайти спосіб вирішити цю проблему.
— Індіана Кернік

2
Ви можете використовувати нестандартний __COUNTER__макрос для цього. Щось подібне static_assert(__COUNTER__ == 0); my_class.my_method();. Однак лічильник скидається в кожну одиницю перекладу, щоб ви могли лише перевірити, чи функція викликається один раз на одиницю перекладу.
— Індіана Кернік

4
Чому ти хочеш це робити? Частина точки функції полягає в тому, що її можна викликати з декількох місць.
— Чіпстер

4
Ви повинні пояснити, чому ви хочете це зробити. Можливо, рішення, про яке ви просите, не найкраще для досягнення ваших реальних цілей.
— десять четвертого

Відповіді:


6

Низький технічний підхід:

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

  • зробити назву функції достатньо унікальною
  • grep для імені функції у вашому коді. Ви очікуєте цього двічі (якщо вважати, що декларація та визначення є кольоровими):
    • Одного разу в заголовку
    • Одного разу на сайті одного дзвінка

Як варіант:

Якщо ви дійсно дуже хочете вирішити це за допомогою C ++, то можете спробувати

  • Використовуйте лічильник часу компіляції, щоб визначити кількість використання в одиницях компіляції
  • Переконайтеся, що функція порушить ODR, якщо заголовок включений у кілька одиниць компіляції.

Однак лічильники часу компіляції - це чорна магія (каже, що мені дуже подобається TMP), і примушування порушень ODR для цієї мети здається схожим на вуду (принаймні, вам знадобиться тестовий випадок, який не вдається зв’язати).

Але серйозно:

Не робіть цього. Що б ви не робили, це може перешкоджати майже без зусиль функцією обгортки:

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

MyClass::my_method()називається тільки в обгортці. Всі інші просто називають обгортку, яку, мабуть, навіть накреслив компілятор.

Як запропонували інші: Це може бути набагато кориснішим, якщо ви поясните, що ви намагаєтеся зробити.


1

Ось приблизна ідея, яка може спрацювати (занадто довго для коментарів, але неповна для гарної відповіді ТА).

Ви можете досягти цього за допомогою підрахунку / перевірки екземплярів шаблону.
Шаблони створюються миттєво лише під час використання .

Аналогічно, тіло методу / функції функцій шаблону не розбираються, не компілюються і не пов'язані (крім забезпечення дійсного синтаксису), якщо вони ніколи не викликаються. Це означає, що будь-які моменти всередині їхніх органів не робляться).

Можливо, ви зможете створити шаблон, який підтримує деякий глобальний підрахунок інстанцій і статичне твердження на цьому (або якийсь інший механізм 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;
}

Це рішення є частковим у тому сенсі, що не заважає програмі викликати метод, що починається з підкреслення безпосередньо, без використання макросу обгортки.


0

Використовуйте лічильник constexpr. В іншому питанні є реалізація


1
Здається, цей метод неправильно сформований.
— Чіпстер

Питання open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118, на яке посилається у відповіді на це запитання, стверджує, що це помилка стандарту і його слід неправильно формувати.
— SD57

Так це не погано сформовано, принаймні ще ні?
— Чіпстер

Якщо він ще не сформований, його слід використовувати якомога швидше якомога більше людей, щоб вони мали підтримати цю справу використання!
— користувач1685095
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.