Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.85/41: Рейтинг темы: голосов - 41, средняя оценка - 4.85
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
1

Дизайн и эволюция: перегрузка макросов

10.02.2016, 04:10. Показов 7684. Ответов 17
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Часть 0. Вместо предисловия.

всем привет.

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

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

у меня ушло некоторое время,
что бы нагуглить портируемое решение:

cl (msvc2012/msvc2013/msvc2015),
mingw(481/492),
gcc (493),
clang (3.7.0)

пришлось прочитать уйму материалов.

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

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

итак, поехали.

-------------------------------------------
Часть 1. Основа.

для начала, нужно понять одно простое правило:
как раскрываются макросы?


что значит "раскрывается"?
это значит, в то место, где был макрос,
препроцессор подставляет результирующий текст.

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

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

пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
 
 
#define dSECOND(n) std::cout << "n = "<<n<<std::endl
 
#define dFISRT(n) dSECOND(n); dSECOND(n+1)
 
int main()
{
    std::cout << "Hello, world!\n";
    dFISRT(10);
    
    // шаг 1. dFISRT(10)    ---> dSECOND(10); dSECOND(10+1)
    // шаг 2. dSECOND(10);   ---> std::cout << "n = "<< 10 << std::endl;
    //        dSECOND(10+1); ---> std::cout << "n = "<< 10+1 <<std::endl;
    
}
при раскрытии макроса dFISRT(10)
образовался текст вызова другого макроса.
поэтому, препроцессор продолжил раскрывать
уже вложенный макрос

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

собственно на этом вся великая магия препроцессора и заканчивается.

однако этого оказалось вполне достаточно,
что бы разные умельцы смогли проворачивать
свои темные причудливые делишки.

поехали дальше.

-------------------------------------------
Часть 2. Макросы с переменным количеством аргументов.

синтаксис очень простой:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
 
#define dMACRO(...) foo(__VA_ARGS__)
 
template<class... Args>
void foo(Args&&... args)
{
    std::cout << "number of arguments = " 
        << sizeof...(args)<<std::endl;
}
 
int main()
{
    std::cout << "Hello, world!\n";
    dMACRO(1,2,3);
 
    // dMACRO(1,2,3);  ---> foo(1,2,3);
}
тут все понятно: указав троеточие в макросе,
мы можем передать неограниченное количество аргументов

внутри макроса их обозначает специальный дефайн __VA_ARGS__
причем текст передается "как есть"
со всеми указанными запятыми.

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

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

-------------------------------------------
Часть 3. Определяем количество переданных аргументов.

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

в этом случае __VA_ARGS__ раскрывается в пустоту.

нижеследующий способ не умеет обрабатывать такую ситуацию.
далее, я покажу более правильный способ.

но к нему мы придем постепенно:
от более простого к сложному.

итак, я просто покажу реализацию на примере.

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

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
 
// ==============================================================
 
// вычисляет количество аргументов переданных макросу
 
#define ARG_N( _1, _2, _3, _4, _5, N, ...) N
#define CONVERT(args) ARG_N args
#define NARG(...) CONVERT( (__VA_ARGS__ , 5,4,3,2,1,0) )
 
// ==============================================================
 
int main()
{
    std::cout << "Hello, world!\n";
    
    // не умеет правильно определять 0 аргументов
    std::cout << NARG() <<std::endl;
    
    // шаг 1. NARG() ---> CONVERT( ( , 5,4,3,2,1,0) )
    
    // шаг 2. CONVERT( ( , 5,4,3,2,1,0) ) ---> ARG_N ( , 5, 4, 3, 2, 1, 0 )
    
    // шаг 3. ARG_N (  ,  5, 4 ,  3,  2,  1, 0 ) ---> 1
    //               _1, _2, _3, _4, _5,  N, ...
    // результат 1    
    
    std::cout << NARG(A) <<std::endl;
    
    // шаг 1. NARG(A) ---> CONVERT( (A , 5,4,3,2,1,0) )
    
    // шаг 2. CONVERT( (A , 5,4,3,2,1,0) ) ---> ARG_N ( A, 5, 4, 3, 2, 1, 0 )
    
    // шаг 3. ARG_N ( A,  5,  4,  3,  2, 1, 0 ) ---> 1
    //               _1, _2, _3, _4, _5, N, ...
    // результат 1
    
    std::cout << NARG(A,B) <<std::endl;
    
    // шаг 1. NARG(A,B) ---> CONVERT( (A,B, 5,4,3,2,1,0) )
    
    // шаг 2. CONVERT( (A,B, 5,4,3,2,1,0) ) ---> ARG_N ( A,B, 5, 4, 3, 2, 1, 0 )
    
    // шаг 3. ARG_N ( A,  B,  5,  4,  3, 2, 1, 0 ) ---> 2
    //               _1, _2, _3, _4, _5, N, ...
    // результат 2
 
}
на что здесь нужно обратить внимание?
собака зарыта в макросе CONVERT
благодаря нему происходит формирование вызова макроса ARG_N
он заставляет __VA_ARGS__ раскрыться в результирующий текст
и таким образом образуется вызов макроса ARG_N
с несколькими аргументами.

-------------------------------------------
Часть 4. Zero

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

однако есть одна хитрость,
благодаря которой это можно исправить.

если написать вот так:

C++
1
MACRO __VA_ARGS__ ()
и при этом __VA_ARGS__ будет содержать пустоту,
то все выражение схлопнется, и образуется:
C++
1
MACRO()
тобишь - вызов вложенного макроса.

и это - кроссплатформенно.

наверняка, человек который до такого додумался - настоящий ценитель!

("настоящими ценителями" я называю конченных извращенцев,
если кто не понял)

таким образом,
предыдущую реализацию можно переписать вот так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
 
// ==============================================================
 
// вычисляет количество аргументов переданных макросу
 
#define NO_ARGS() ,,,,,0
 
#define ARG_N( _1, _2, _3, _4, _5, N, ...) N
 
#define CONVERT(args) ARG_N args
    
#define ZERO(...) CONVERT( (__VA_ARGS__ , 5,4,3,2,1,0) )
 
// если __VA_ARGS__ - пусто, тогда макрос схлопнется, 
// и получится NO_ARGS()
#define NARG(...) ZERO( NO_ARGS __VA_ARGS__ ()  )
 
// ==============================================================
 
int main()
{
    std::cout << "Hello, world!\n";
    
    
    std::cout << NARG() <<std::endl;
    
    // шаг 1. NARG() ---> ZERO( NO_ARGS()  )
    
    // шаг 2. ZERO( NO_ARGS()  ) ---> ZERO( ,,,,,0 )
    
    // шаг 3. ZERO( ,,,,,0 ) ---> CONVERT( (,,,,,0 , 5,4,3,2,1,0) )
    
    // шаг 4. CONVERT( (,,,,,0 , 5,4,3,2,1,0) ) ---> ARG_N ( ,,,,,0 , 5,4,3,2,1,0)
    
    // шаг 5. ARG_N (  ,   ,   ,   ,   ,  0, 5,4,3,2,1,0) ---> 0
    //               _1, _2, _3, _4, _5,  N, ...
    // результат 0    
    
    std::cout << NARG(A) <<std::endl;
    
    // шаг 1. NARG(A) ---> ZERO( NO_ARGS A()  )
    
    // шаг 2. ZERO( NO_ARGS A ()  ) ---> CONVERT( (NO_ARGS A () , 5,4,3,2,1,0) )
    
    // шаг 3. CONVERT( (NO_ARGS A () , 5,4,3,2,1,0) ) ---> ARG_N ( NO_ARGS A () , 5,4,3,2,1,0)
    
    // шаг 5. ARG_N ( NO_ARGS A (),  5,  4,  3,  2,  1,  0) ---> 1
    //                          _1, _2, _3, _4, _5,  N, ...
    // результат 1    
    
    std::cout << NARG(A,B) <<std::endl;
    
    // шаг 1. NARG(A,B) ---> ZERO( NO_ARGS A,B ()  )
    
    // шаг 2. ZERO( NO_ARGS  A,B ()  ) ---> CONVERT( (NO_ARGS A, B () , 5,4,3,2,1,0) )
    
    // шаг 3. CONVERT( (NO_ARGS A, B () , 5,4,3,2,1,0) ) ---> ARG_N ( NO_ARGS A, B () , 5,4,3,2,1,0)
    
    // шаг 5. ARG_N ( NO_ARGS A, B (),  5,  4,  3,  2,  1,  0) ---> 2
    //                       _1,   _2, _3, _4, _5,  N, ...
    // результат 2    
}
как видите - все гениальное просто.

-------------------------------------------
Часть 5. Перегрузка макросов.

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

теперь мы готовы.

суть идеи простая:

допустим, у нас есть макрос:
C++
1
#define ACTION(...)
пишем несколько перегрузок под него:

C++
1
2
3
ACTION_0()      /* код для нуля аргументов */
ACTION_1(a1)    /* код для 1 аргумента     */
ACTION_2(a1,a2) /* код для 2 аргументв     */
и тд.

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

из под главного макроса,
просто сделаем вызов вложенного макроса-перегрузки
подставив в его имя текст циферки - количество аргументов.
а в качестве аргументов укажем __VA_ARGS__

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// ===================================================================================
// ===================================================================================
// ===================================================================================
 
    // workaround.h
 
    // --- поддержка до 7 аргументов включительно
    #define dFUNC_CHOOSER_7(_f1, _f2, _f3, _f4, _f5, _f6, _f7, N, ... ) N
 
    #define dFUNC_RECOMPOSER(argsWithParentheses)\
        dFUNC_CHOOSER_7 argsWithParentheses
 
    #define dMACRO_CHOOSER(target_, ...)\
        dCHOOSE_FROM_ARG_COUNT(target_, target_##_NO_ARG_EXPANDER __VA_ARGS__ ())
 
    #define dCHOOSE_FROM_ARG_COUNT(arg_, ...) \
        dFUNC_RECOMPOSER((__VA_ARGS__, arg_##_7, arg_##_6, arg_##_5, arg_##_4, arg_##_3, arg_##_2, arg_##_1, ))
 
// ===================================================================================
// ===================================================================================
// ===================================================================================
 
    // реализации перегруженного макроса под различное количество аргументов
    #define dACTION_7(a1, a2, a3, a4, a5, a6, a7) \
        std::cout << "7:ACTION(" << a1 << "," << a2 << "," << a3 << "," << a4 << ","<< a5 << "," << a6 << "," << a7 << ")\n"
 
    #define dACTION_6(a1, a2, a3, a4, a5, a6) \
        std::cout << "6:ACTION(" << a1 << "," << a2 << "," << a3 << "," << a4 << ","<< a5 << "," << a6 << ")\n"
 
    #define dACTION_5(a1, a2, a3, a4, a5) \
        std::cout << "5:ACTION(" << a1 << "," << a2 << "," << a3 << "," << a4 << ","<< a5 << ")\n"
 
    #define dACTION_4(a1, a2, a3, a4) \
        std::cout << "4:ACTION(" << a1 << "," << a2 << "," << a3 << "," << a4 << ")\n"
 
    #define dACTION_3(a1, a2, a3) \
        std::cout << "3:ACTION(" << a1 << "," << a2 << "," << a3 << ")\n"
 
    #define dACTION_2(a1, a2) \
        std::cout << "2:ACTION(" << a1 << "," << a2 << ")\n"
 
    #define dACTION_1(a1) \
        std::cout << "1:ACTION(" << a1 << ")\n"
 
    #define dACTION_0() \
        std::cout << "0:ACTION()\n"
    
 
    #define dACTION_NO_ARG_EXPANDER() \
        ,,,,,,,dACTION_0
 
    #define ACTION(...)\
        dMACRO_CHOOSER( dACTION, __VA_ARGS__)(__VA_ARGS__)
 
// ===================================================================================
// ===================================================================================
// ===================================================================================
    
#include <iostream>
 
int main()
{
    std::cout << "Hello, world!\n";
    
    ACTION();
    ACTION(1);
    ACTION(1,2);
    ACTION(1,2,3);
    ACTION(1,2,3,4);
    ACTION(1,2,3,4,5);
    ACTION(1,2,3,4,5,6);
    ACTION(1,2,3,4,5,6,7);
}
единственное, что нового в представленной итоговой версии:
я выделил общую одинаковую для разных макросов деталь.

что бы максимум кода можно было вынести в библиотеку костыль workaround.h
для повторного использования.

от программиста требуется только описать сами перегрузки,
трамплин для нуля аргументов, и префиксы имен перегрузок:


C++
1
2
3
4
5
6
7
8
// опишите трамплин для нуля аргументов
#define dACTION_NO_ARG_EXPANDER() \
        ,,,,,,,dACTION_0
 
 
// укажите префикс для перегрузок
#define ACTION(...)\
    dMACRO_CHOOSER( dACTION, __VA_ARGS__)(__VA_ARGS__)
ну и напоследок, я привожу ссылку на материал,
который послужил для меня первоисточником.
http://stackoverflow.com/quest... h-c-macros

на этом все.
пока.
13
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.02.2016, 04:10
Ответы с готовыми решениями:

[дизайн и эволюция] провалы в variadic конструкторы
всем привет. уже несколько человек обращались ко мне по почте, с просьбой помочь разобраться с...

[Дизайн и эволюция] Дискриминация шаблона на примере макроса OUT_TO_STREAM
рублика: дизайн и эволюция название: дискриминация шаблона на примере макроса OUT_TO_STREAM ...

Дизайн сайтов (desktop и адаптивный дизайн), баннеров и логотипов
Добрый день! Меня зовут Катя. Я - начинающий дизайнер. Рисую за гроши сайты (desktop и адаптивный...

Дизайн выпадающего меню под дизайн обычного
Здравствуйте, у меня то есть меню, но, есть одно но... Есть меню в стиле лава-лампы, и я хочу...

17
9 / 5 / 1
Регистрация: 15.08.2016
Сообщений: 48
15.10.2016, 13:41 2
Сами по себе макросы - большое зло, а тут еще их перегрузка
0
Evg
Эксперт CАвтор FAQ
21279 / 8301 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
15.10.2016, 19:27 3
Цитата Сообщение от Sretenkov Посмотреть сообщение
а тут еще их перегрузка
Тут нет перегрузки макросов. Хотя код действительно получается сложно читабельным. В основе лежит техника с более читаемым кодом. Но в этой технике приходится дополнительным параметром передавать количество параметров

C
#define MACRO_0()         ARG0
#define MACRO_1(n1)       ARG1 n1
#define MACRO_2(n1,n2)    ARG2 n1 n2
#define MACRO_3(n1,n2,n3) ARG3 n1 n2 n3
 
#define MACRO(n, ...) MACRO_##n (__VA_ARGS__)
 
MACRO (0)
MACRO (1, a)
MACRO (2, a, b)
MACRO (3, a, b, c)
Код
$ gcc t.c -E
ARG0
ARG1 a
ARG2 a b
ARG3 a b c
Если сюда добавить трюк с вычислением количества аргументов макроса, то и получится такой монстр
1
61 / 5 / 1
Регистрация: 03.06.2013
Сообщений: 354
Записей в блоге: 3
22.06.2019, 01:38 4
Интересная тема, но чтото большая слишком простыня получается

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

задача : перезагрузить 0-2 аргумента МИНИМУМ количества строк. выше предложенный метод конверт или есть еще CONCAT не нравиться своей длиной

пока что я сделал 4 строками но без нулнвого
C++
1
2
3
4
#define GET_MACRO(_1,_2, NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO2, FOO1)(__VA_ARGS__)
#define FOO1(x) ABC(F(x))
#define FOO2(x,y) ABC(F(x), F(y))
и имею два вопроса
1) можно это сделать менее чем 4 строки макроса ?

2) как перегрузить с 0 аргументом в следующей форме
FOO() -> ABC() ?
0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
09.06.2020, 14:52 5
Цитата Сообщение от hoggy Посмотреть сообщение
ACTION();
    ACTION(1);
    ACTION(1,2);
    ACTION(1,2,3);
    ACTION(1,2,3,4);
Полное отсутствие гибкости, а что делать если в макрос нужно передавать код, например вызвать цикл где-то или объявить переменную или передать в макрос макрос что тогда.
C++
1
#define z b+=10; /*вызов в Main*/ACTION(for(;;);,z);
и нужно выполнить по порядку элементы именно в таком виде.

Добавлено через 3 минуты
Так что этот __VA_ARGS__ фигня полная умеет только цифры перебирать. И никакого толку от динамического количества аргументов нет.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.06.2020, 18:14  [ТС] 6
Цитата Сообщение от Nexi99 Посмотреть сообщение
а что делать если в макрос нужно передавать код, например вызвать цикл где-то или объявить переменную или передать в макрос макрос что тогда.
пример использования:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    #define pp_beg(count) \
        ss << "beg{" << count << "}";
 
    #define pp_macro(index, arg, element) \
        ss << "[" << index << ", " << #arg << " VS " << #element "]";
 
    #define pp_delim(index) \
        ss << ",";
 
    #define pp_end(count) \
        ss << '{' << count << "}end";
 
    #define pp_enum(arg, ...)                          \
        pp_beg(dGET_ARGS_COUNT(__VA_ARGS__))           \
        dFOREACH(pp_macro, pp_delim, arg, __VA_ARGS__) \
        pp_end(dGET_ARGS_COUNT(__VA_ARGS__))
 
 
int main()
{
    ::std::stringstream ss;
    pp_enum(Arg, A,B,C,D,F,J);
 
    std::cout << ss.str(); 
}
вывод в консоль:
Код
beg{6}[1, Arg VS A],[2, Arg VS B],[3, Arg VS C],[4, Arg VS D],[5, Arg VS F],[6, Arg VS J]{6}end;
здесь:
dGET_ARGS_COUNT - макрос, который вычисляет кол-во аргументов

dFOREACH - макрос, который в цикле умеет перебирать все переданные ему аргументы,
и что-то с ними делать.

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

так же, становится возможным реализовать поддержку IF на макросах.
что в свою очередь даёт возможность организовать циклы WHILE или FOR на макросах.

лично мне на практике пригодилась возможность
реализовывать различные комбинаторные алгоритмы.
например:

C++
1
2
3
4
5
6
7
8
9
10
11
12
    #define pp_macro(index, a, b) \
        ss << "(" << index << ", " << #a << ":" << #b << ")";
 
    #define pp_delim(index) \
        ss << ", ";
 
... 
 
    ::std::stringstream ss;
    dCOMBINE(pp_macro, pp_delim, (1,2), (3,4,5) );
    const auto result = ss.str();
    std::cout << result;
здесь берется каждый элемент первого кортежа: (1,2)
с каждым элементом второго кортежа: (3,4,5)
и над этой парой выполняется действие, которое описано в макросе pp_macro

вывод в консоль:
Код
(1, 1:3), (2, 1:4), (3, 1:5), (1, 2:3), (2, 2:4), (3, 2:5)
комбинировать аргументы можно по всякому.
не обязательно кортежами.


с точки зрения возможностей кодо-генерации:
вооружившись циклами и условиями,
можно сгенерировать текст любой сложности.

по сути, фантазия программиста ограничена только максимальным кол-вом аргументов,
которое можно передать в макрос: 127 штук.
0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
09.06.2020, 18:30 7
Так что этот __VA_ARGS__ фигня полная умеет только цифры перебирать.
Цитата Сообщение от hoggy Посмотреть сообщение
можно сгенерировать текст любой сложности.
А как сгенрировать мой текст чтобы выполнилось for(;;)break;b+=10; ваши примеры это игра с цифрами и текстом а я спрашиваю о том что я запихиваю код в макрос а нутри он по порядку весь выполянется. Это походу интерпретация тут наверное только Пион сможет такое, хотя что сложного в том выполнить по порядку аргументы макроса но видимо сложно. Получается что эффективность макросов с неограниченными параметрами нулевая.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.06.2020, 19:26  [ТС] 8
Цитата Сообщение от Nexi99 Посмотреть сообщение
Так что этот __VA_ARGS__ фигня полная умеет только цифры перебирать.
твоя проблема в том, что ты пытаешься критиковать тему, в которой ни бум бум.

во-первых,
с чего ты взял, будто бы __VA_ARGS__ вообще что-то там перебирает?
вот откуда в твоей голове взялся этот бред?

во-вторых,
вот с чего ты взял, что __VA_ARGS__ можно составить только из цифр?

такое впечатление, что ты вообще не понимаешь, что такое __VA_ARGS__,
и зачем он нужен.

я думаю, тебе нужно открыть букварь, главу "препроцессор",
и для начала изучить азы.
1
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
09.06.2020, 23:16 9
Цитата Сообщение от hoggy Посмотреть сообщение
__VA_ARGS__
Я долго пытался найти что-то про этот элемент но как я посмотрю работает он только с цифрами и буквами или процедурными элементами спору нет вызвать функции он может по порядку, но я вас спрашиваю как вызвать этот код for(;;)break;b+=10;, __VA_ARGS__ тут не катит получается что он и создан для перебора цифр и букв, все примеры включая ваш примитивны, возможно потому что сами макросы таковы примитивны поэтому и нельзя выполнить такую строку ACTION(for(;;)break;,z); вызов должен быть for(;;)break;b+=10; это и есть написание текстового кода в макросе да даже такой код оно не выполнит ACTION(;,;,;);, как только программа натыкается на символ ; между запятыми сразу же облом, вот так вот а писать цифры это да можно и в функции для этого макрос не нужен.
Макросы это первое с чем можно работать потому что остальное на много сложнее а макрос некий интерпретатор.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.06.2020, 23:42  [ТС] 10
Цитата Сообщение от Nexi99 Посмотреть сообщение
спору нет вызвать функции он может по порядку
я ещё раз задам тебе вопрос:

Цитата Сообщение от hoggy Посмотреть сообщение
с чего ты взял, будто бы __VA_ARGS__ вообще что-то там перебирает?
вот откуда в твоей голове взялся этот бред?
Цитата Сообщение от Nexi99 Посмотреть сообщение
ACTION(for(;break;,z);
Цитата Сообщение от Nexi99 Посмотреть сообщение
ACTION(;,;,;
Цитата Сообщение от Nexi99 Посмотреть сообщение
как только программа натыкается на символ ;
https://rextester.com/HCOTY46347

C++
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
 
#define dACTION(...) \
    std::cout << # __VA_ARGS__ << '\n'
 
int main()
{
    dACTION(for);
    dACTION(for(;;)break;b+=10;);
    dACTION(void();,void(););    
}
вывод в консоль:
Код
for
for(;;)break;b+=10;
void();,void();
0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
10.06.2020, 01:52 11
Цитата Сообщение от hoggy Посмотреть сообщение
с чего ты взял, будто бы __VA_ARGS__ вообще что-то там перебирает?
Ну я так просто выразился. И это может быть не __VA_ARGS__ , а какая-нибудь другая запись.
Цитата Сообщение от hoggy Посмотреть сообщение
вывод в консоль:
Значит возможность макросов с неограниченным количеством аргументов сводится только в выводе в консоль, меня в частности интересует выполнение аргумента а не вывод в консоль вот в чём моя идея, печатать в консоль можно и стихотворения, но меня интересует выполнения этого текста не в виде строки. Т.е. сначала запускается цикл for(;;)break; а потом происходит выход а потом выполняется этот код b+=10;( при этом b может быть как объявлено так и нет int b+=10;). Классная задумка да.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
10.06.2020, 05:26  [ТС] 12
Цитата Сообщение от Nexi99 Посмотреть сообщение
Ну я так просто выразился. И это может быть не __VA_ARGS__, а какая-нибудь другая запись.
то, что ты пишешь, или печатаешь в виде текста - это твои мысли.
если в тексте каша, значит в голове тоже каша.

какая ещё такая "другая" запись?
ты сам то хоть понимаешь о чем думаешь?

Цитата Сообщение от Nexi99 Посмотреть сообщение
Значит возможность макросов с неограниченным количеством аргументов сводится только в выводе в консоль
нет, не сводится.
с чего ты взял своё "значит" ?

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

иначе как ещё можно объяснить тот факт,
что ты до сих пор так и не понял,
что препроцессор тупо подставляет обычный текст?

https://rextester.com/AUP28595

C++
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
 
#define dACTION(...)  __VA_ARGS__ 
 
int main()
{
    int b = 10;
    
    dACTION(for(;;)break;b+=10);
    
    std::cout << "b = " << b << " (must be 20)\n";
}
вывод в консоль:
Код
b = 20 (must be 20)
после препроцессирования:

C++
1
2
3
4
5
    int b = 10;
    
    for(;;)break;b+=10;
    
    std::cout << "b = " << b << " (must be 20)\n";
Цитата Сообщение от Nexi99 Посмотреть сообщение
Классная задумка да.
если у тебя что-то не получается, тогда создай тему в разделе для новичков.
выложи человеческое словесное описание.
выложи код, который не получается.

но не нужно приходить в мою тему,
и называть __VA_ARGS__ фигнёй только потому,
что ты его не осилил.
0
Croessmah
10.06.2020, 11:24
  #13

Не по теме:

Цитата Сообщение от hoggy Посмотреть сообщение
если у тебя что-то не получается, тогда создай тему в разделе для новичков.
Она у него есть: Как использовать макросы с неограниченным количеством аргументов?
Каюсь, отправил его сюда почитать...

0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
10.06.2020, 11:39 14
А сделать такое сможет.
C++
1
#define kl(...) for(int q=-1;++q<3;){/*выполняем аргументы по порядку/ по одному*/}
Вызов в Main()
C++
1
2
int b = 10;
kl(for(;;)break;,b+=10;)
Сначала выполняется аргумент for(;;)break;, потом выполняется b+=10; у меня бывает нужда в таких конструкциях приходится по отдельности выполнять и цикл for(int q=-1;++q<3;) вызывать 2 раза, первый раз чтобы выполнить аргумент for(;;)break;, второй раз чтобы выполнить b+=10;. Мне нужно было сразу так объяснить.
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
10.06.2020, 14:08 15
Так что ли?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
 
#define EXECUTE(...) EXECUTE_AUX(__VA_ARGS__)
#define EXECUTE_AUX(...) EXECUTE_ALL(NARG(__VA_ARGS__), __VA_ARGS__)
#define EXECUTE_ALL(N, ...) EXECUTE_CHOOSE_MACRO(N, __VA_ARGS__)
#define EXECUTE_CHOOSE_MACRO(N, ...) EXECUTE_AUX_##N(__VA_ARGS__)
 
 
#define EXECUTE_AUX_10(Arg, ...) EXECUTE_AUX_1(Arg) EXECUTE_AUX_9(__VA_ARGS__)
#define EXECUTE_AUX_9(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_8(__VA_ARGS__)
#define EXECUTE_AUX_8(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_7(__VA_ARGS__)
#define EXECUTE_AUX_7(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_6(__VA_ARGS__)
#define EXECUTE_AUX_6(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_5(__VA_ARGS__)
#define EXECUTE_AUX_5(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_4(__VA_ARGS__)
#define EXECUTE_AUX_4(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_3(__VA_ARGS__)
#define EXECUTE_AUX_3(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_2(__VA_ARGS__)
#define EXECUTE_AUX_2(Arg, ...)  EXECUTE_AUX_1(Arg) EXECUTE_AUX_1(__VA_ARGS__)
#define EXECUTE_AUX_1(Arg)       EXECUTE_CODE(Arg)
#define EXECUTE_AUX_0()          (void)
 
#define EXECUTE_CODE(Arg) Arg;
 
#define ARG_N( _1, _2, _3, _4, _5, N, ...) N
#define NARG(...) CONVERT( (__VA_ARGS__ , 5,4,3,2,1,0) )
#define CONVERT(args) ARG_N args
 
 
int main()
{
    EXECUTE(
        std::cout << "xxx" << std::endl, 
        std::cout << "yyy\n", 
        for(int i = 0; i < 10; ++i) 
            std::cout << i << "\n"
    );
}
xxx
yyy
0
1
2
3
4
5
6
7
8
9
https://wandbox.org/permlink/zIHPDbbbCnXfb7P4

P.S. Там косяк с ;


Добавлено через 20 минут
Чуть "модернизировал". Кажется, работает и это как раз то, чего ты хочешь. Но это не точно.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
 
#define EXECUTE(Macro, ...) EXECUTE_AUX(Macro, __VA_ARGS__)
#define EXECUTE_AUX(Macro, ...) EXECUTE_ALL(Macro, NARG(__VA_ARGS__), __VA_ARGS__)
#define EXECUTE_ALL(Macro, N, ...) EXECUTE_CHOOSE_MACRO(Macro, N, __VA_ARGS__)
#define EXECUTE_CHOOSE_MACRO(Macro, N, ...) EXECUTE_AUX_##N(Macro, __VA_ARGS__)
 
 
#define EXECUTE_AUX_10(Macro, Arg, ...) EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_9(Macro, __VA_ARGS__)
#define EXECUTE_AUX_9(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_8(Macro, __VA_ARGS__)
#define EXECUTE_AUX_8(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_7(Macro, __VA_ARGS__)
#define EXECUTE_AUX_7(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_6(Macro, __VA_ARGS__)
#define EXECUTE_AUX_6(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_5(Macro, __VA_ARGS__)
#define EXECUTE_AUX_5(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_4(Macro, __VA_ARGS__)
#define EXECUTE_AUX_4(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_3(Macro, __VA_ARGS__)
#define EXECUTE_AUX_3(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_2(Macro, __VA_ARGS__)
#define EXECUTE_AUX_2(Macro, Arg, ...)  EXECUTE_AUX_1(Macro, Arg) EXECUTE_AUX_1(Macro, __VA_ARGS__)
#define EXECUTE_AUX_1(Macro, Arg)       Macro(Arg)
 
#define EXECUTE_ARGS_3TIMES(Arg) for (int i = 0; i < 3; ++i) { Arg }
 
#define ARG_N( _1, _2, _3, _4, _5, N, ...) N
#define NARG(...) CONVERT( (__VA_ARGS__ , 5,4,3,2,1,0) )
#define CONVERT(args) ARG_N args
 
 
int main()
{
    EXECUTE(EXECUTE_ARGS_3TIMES,
        std::cout << "xxx" << std::endl;, 
        std::cout << "yyy\n";, 
        for(int i = 0; i < 10; ++i) 
            std::cout << i << " ";
        std::cout << "\n";
    )
}
xxx
xxx
xxx
yyy
yyy
yyy
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
Но учти, что применение этого добра в повседневном коде будет чудовищной ошибкой.
https://wandbox.org/permlink/O3PgHnM1QhjxyteZ
0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
10.06.2020, 15:17 16
Цитата Сообщение от Croessmah Посмотреть сообщение
это как раз то, чего ты хочешь
Да это то, чувствую что то.
Цитата Сообщение от Croessmah Посмотреть сообщение
Но учти, что применение этого добра в повседневном коде будет чудовищной ошибкой.
Почему (UB) а зачем же эти языки программирования нужны. У меня есть программы где выбиваются предупреждения связанные с конвертацией типов, но либо новую переменную объявить либо предупреждение всё одно и тоже.
Также я удаляю макросы
C++
1
2
3
4
#undef EXECUTE_AUX_10 EXECUTE_AUX_9
//так тоже пудрит мозги по идее так надо
 #undef EXECUTE_AUX_10 
 #undef EXECUTE_AUX_9
ну оно удяляется работает да и зачем громодить лишню строку кода? в mql4 можно так писать
{} #undef EXECUTE_AUX_10 #undef EXECUTE_AUX_9
А ещё плохо что приходится писать EXECUTE_AUX_10/9/8 константность какаета.

Добавлено через 2 минуты
Цитата Сообщение от Croessmah Посмотреть сообщение
#define ARG_N( _1, _2, _3, _4, _5, N, ...) N
#define NARG(...) CONVERT( (__VA_ARGS__ , 5,4,3,2,1,0) )
И эти цифры блин 5,4,3,2,1,0 делают программу не универсальной, хреново это конечно. Как вариант прямо в коде удалить и переписать макрос и тогда получиться динамический макрос
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
10.06.2020, 16:28 17
Цитата Сообщение от Nexi99 Посмотреть сообщение
Почему (UB) а зачем же эти языки программирования нужны.
UB здесь причем? Такую штуку можно использовать где-нибудь в недрах библиотеки, чтобы построить наверху что-то хорошее. Но использовать это наверху - это просто говнокод получится и всё.
Короче, фигня всё это. Ты просто хочешь не нормального.

Не по теме:

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

0
143 / 27 / 4
Регистрация: 06.05.2019
Сообщений: 1,790
Записей в блоге: 4
10.06.2020, 16:55 18
Цитата Сообщение от Croessmah Посмотреть сообщение
Ты просто хочешь не нормального.
Ну так а что, я по возможности сокращаю код макрасами, но и макросы нужно делать как можно более универсальные чтобы выполнялось правило 3ёх и чтобы выражений включало это дело как можно больше. Ну тут да спору нет читабильность куда сильно ухудшается, но его же делаешь не для того чтобы читать, он либо на мусорку либо работает и лезть в налаженный механизм уже нет нужды. Спасибо за подсказки. Было бы ещё классно придумать что-то #define EXECUTE_AUX_10/9/8 чтобы не писать все 10 штук и для функций это было бы классно. Ну тут по ходу уже ничего не сделаешь. Бывает даже функции пишешь int Int_1(){return 8;}Int_2()/3 и т.д. Вроде типа сделать чтобы всё было как массивы цифры.
0
10.06.2020, 16:55
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.06.2020, 16:55
Помогаю со студенческими работами здесь

Эволюция
Я уже 1 год изучаю delphi но только сетевую част. я хоте бы узнать что будит нужным для работы на...

Эволюция растений
Почему за столько лет растения так и не научились доставать азот из воздуха?

Эволюция и человек
Я не сторонник эволюции, но за неимением более внятного объяснения происхождения видов ударимся в...

Эволюция поверхности
Помогите решить задачку. Суть - нужно построить график или (в идеале) анимацию развития...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru