макрос, который применяется только один раз

#c

#c

Вопрос:

Можно ли написать макрос, который применяется ровно один раз, независимо от того, сколько раз он вложен?

 // how to implement this properly?
#define FOO(x) hello(x) // incorrect, works only for 1 level of nesting

// desired use below
void hello(int x);
FOO(x); // expanded to "hello(x)"
FOO(FOO(x)); // also expanded to "hello(x)", not "hello(hello(x))"
FOO(FOO(FOO(x))); // also expanded to "hello(x)"
 

Существуют ли какие-либо альтернативы реализации FOO , возможно, не как макрос, а какой-либо шаблон C , который может достичь того же эффекта?


Зачем мне это нужно?

Я пишу преобразования clang libTooling от источника к источнику, которые включают некоторые переменные, используемые в моей собственной оболочке. Например, у меня есть следующий фрагмент кода: a[3] = 5;

он преобразуется в hello(a)[3] = 5; , а значение и поведение hello контролируются мной. Однако, если у меня есть следующий фрагмент кода

 #define A a[3] 
A = 4;
A = 5;
A = 6;
 

затем из-за ограничений clang libTooling я должен добавить изменения в тело макроса следующим образом:

 #define A hello(a)[3] 
A = 4;
A = 5;
A = 6;
 

К сожалению, этого нелегко достичь, потому что шаблон сопоставляется 3 раза, и не очевидно, как их различать в общем случае, поэтому все заканчивается так:

 #define A hello(hello(hello(a)))[3] 
A = 4;
A = 5;
A = 6;
 

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

Комментарии:

1. AFAIK, нет. Если вы хотите запустить что-то один раз, вам следует изучить std::call_once

2. Зачем вам что-то подобное? Просто не вводите FOO(FOO .

3. Пожалуйста, опишите, что вы пытаетесь сделать более подробно, с учетом контекста.

4. @KamilCuk Я добавил объяснение моего варианта использования для него.

5. Предварительная обработка — это облегченный инструмент для удобства, а не полноценный инструмент для программирования. Избегайте возиться с ним.

Ответ №1:

Вы можете перегрузить функцию по типу аргумента. С помощью специального типа вы можете просто переслать его, вызывая функцию только тогда, когда указаны фактические аргументы.

 void hello(int x);

struct FOO_hello_called {};
FOO_hello_called FOO(int x) {
   hello(x);
   return {};
}
FOO_hello_called FOO(FOO_hello_called s) {
   return s;
}

int main() {
    int x = 1;
    FOO(x); // calls "hello(x)"
    FOO(FOO(x)); // calls "hello(x)" once
    FOO(FOO(FOO(x))); // also calls "hello(x)" once
}