Возможно ли частичное применение макросов / каррирование в препроцессоре C?


В качестве примера проблемы, есть ли способ реализовать макрос partialconcat в следующем коде?

#define apply(f, x) f(x)

apply(partialconcat(he),llo) //should produce hello

Правка:

Вот еще один пример, заданный FOR_EACH вариадическим макросом (см. Пример реализации в этого ответа на другой вопрос).

Скажем, я хочу вызвать элемент на нескольких объектах, вероятно, внутри другого макроса для более важной цели. Мне бы хотелось, чтобы макрос callMember вел себя следующим образом:

FOR_EACH(callMember(someMemberFunction), a, b, c);

Производит

a.someMemberFunction(); b.someMemberFunction(); c.someMemberFunction();

Это требуется callMember(someMember), чтобы создать макрос, который ведет себя как

#define callMember_someMember(o) o.someMember()
3 3

3 ответа:

Препроцессор C-это "всего лишь" простой текстовый процессор. В частности, один макрос не может определить другой макрос; вы не можете создать #define из расширения макроса.

Я думаю, что это означает, что последние две строки вашего вопроса:

Это нужно callMember(someMember), чтобы создать макрос, который ведет себя как

#define callMember_someMember(o) o.someMember()

Не достижимы при одном применении препроцессора C (и, в общем случае, вам нужно было бы применить препроцессор an). произвольное число раз, в зависимости от того, как определяются макросы).

Вы можете достичь желаемого результата с помощью препроцессора, используя невероятный язык/библиотеку "порядка" Vesa Karvonen: http://rosettacode.org/wiki/Order

Это работает путем реализации целого второго языка высокого уровня поверх самого препроцессора, с поддержкой таких вещей, как каррирование и первоклассные макросы и так далее. Это довольно тяжелая работа, хотя нетривиальный код заказа занимает очень много времени для компиляции, потому что CPP не был разработан для использования в таким образом, и большинство компиляторов C не могут справиться с этим. Он также очень хрупок: ошибки во входном коде, как правило, приводят к непонятной тарабарщине на выходе.

Но да, это можно сделать, и сделать за один проход препроцессора. Это простонамного сложнее, чем вы могли бы ожидать.

Используйте макросы более высокого порядка:

#define OBJECT_LIST(V) \
    V(a) \
    V(b) \
    V(c)

#define MEMBER_CALL(X) \
    X.some_func();


OBJECT_LIST(MEMBER_CALL)

Вывод

$ g++ -E main.cc
# 1 "main.cc"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cc"
# 10 "main.cc"
a.some_func(); b.some_func(); c.some_func();

Поскольку это цикл времени компиляции, карринг затруднен. макрос OBJECT_LIST определяет, сколько аргументов разрешено использовать каждому пользователю этого списка. аргументы вызова функции (по умолчанию) являются частью команды define then. Вы можете свободно отказаться от использования аргумента по умолчанию или использовать постоянное значение самостоятельно. Я не смог найти правильный способ уменьшить количество аргументов в препроцессоре. Этот факт ограничивает общность этой техники.

#define OBJECT_LIST(V) \
    V(a, 1,2,3) \
    V(b, 4,5,6)

#define MEMBER_CALL(X, A1, A2, A3) \
    X.somefunc(A1, A2, A3);

#define CURRY_CALL(X, A1, A2, A3) \
    X.somefunc(A1, 2, 2);

#define NO_CURRY_CALL(X, A1, A2, A3) \
    X.xomefunc(A1);


OBJECT_LIST(MEMBER_CALL)
OBJECT_LIST(CURRY_CALL)
OBJECT_LIST(NO_CURRY_CALL)

Вывод:

# 1 "main2.cc"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main2.cc"
# 12 "main2.cc"
a.somefunc(1, 2, 3); b.somefunc(4, 5, 6);
a.somefunc(1, 2, 2); b.somefunc(4, 2, 2);
a.somefunc(1); b.somefunc(4);