8971 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
1 | |||||||||||||||||||||||||||||||||||||||||||||||||||
Дизайн и эволюция: перегрузка макросов10.02.2016, 04:10. Показов 8086. Ответов 17
Метки нет (Все метки)
Часть 0. Вместо предисловия.
всем привет. недавно, для одной из моих задач, мне потребовалось иметь возможность перегрузить макрос под разное количество аргументов. и хотя на самом деле препроцессор не поддерживает такую возможность. однако, существуют такие экзотические техники, которые позволяют провернуть подобное. у меня ушло некоторое время, что бы нагуглить портируемое решение: cl (msvc2012/msvc2013/msvc2015), mingw(481/492), gcc (493), clang (3.7.0) пришлось прочитать уйму материалов. и хотя прочитанные материалы сильно обогатили мои знания в области препроцессора, но все они без исключения были на английском языке. сами материалы преподносились довольно таки корявенько, поэтому, все равно пришлось сидеть и разбираться. я решил поделиться с сообществом, приобретенным знаниями, преподнося материал на русском языке, и достаточно подробно. так, что бы осилить его смог даже начинающий программист. итак, поехали. ------------------------------------------- Часть 1. Основа. для начала, нужно понять одно простое правило: как раскрываются макросы? что значит "раскрывается"? это значит, в то место, где был макрос, препроцессор подставляет результирующий текст. правило очень простое: препроцессор тупо подставляет кусок текста, который был указан при вызове макроса. если при раскрытии первого макроса, образовался текст вызова другого макроса, то происходит раскрытие и вложенного макроса тоже. и так до тех пор, пока все вложенные друг в друга макросы не раскроются, и не образуется окончательный текст. пример:
образовался текст вызова другого макроса. поэтому, препроцессор продолжил раскрывать уже вложенный макрос здесь ещё стоит обратить внимание, что буковка n внутри литерной константы была проигнорирована несмотря на то, что n - это аргумент макроса, который раскрывается в цыферки. собственно на этом вся великая магия препроцессора и заканчивается. однако этого оказалось вполне достаточно, что бы разные умельцы смогли проворачивать свои поехали дальше. ------------------------------------------- Часть 2. Макросы с переменным количеством аргументов. синтаксис очень простой:
мы можем передать неограниченное количество аргументов внутри макроса их обозначает специальный дефайн __VA_ARGS__ причем текст передается "как есть" со всеми указанными запятыми. __VA_ARGS__ - это на самом деле один такой неделимый аргумент. и если в дальнейшем передать его в макрос с двумя аргументами, то он будет интерпритироваться как один из аргументов такого макроса. то есть, при передаче его в другие макросы, он не раскрывается полностью в текст. это создает некоторые сложности. к этому мы ещё вернемся. теперь, прежде чем мы начнем перегружать макросы, нужно сначала научиться вычислять какое именно количество аргументов было переданно. ------------------------------------------- Часть 3. Определяем количество переданных аргументов. предупреждаю сразу, данный способ - ущербный. дело в том, что в макросы с переменным количеством аргументов, можно вообще вызывать без аргументов. в этом случае __VA_ARGS__ раскрывается в пустоту. нижеследующий способ не умеет обрабатывать такую ситуацию. далее, я покажу более правильный способ. но к нему мы придем постепенно: от более простого к сложному. итак, я просто покажу реализацию на примере. для наглядности я детально расписал каждый шаг раскрытия макросов. логика очень простая, вы сразу же её поймёте
собака зарыта в макросе CONVERT благодаря нему происходит формирование вызова макроса ARG_N он заставляет __VA_ARGS__ раскрыться в результирующий текст и таким образом образуется вызов макроса ARG_N с несколькими аргументами. ------------------------------------------- Часть 4. Zero предыдущая реализация не умеет определять количество аргументов равное нулю. что делает макрос не универсальным. однако есть одна хитрость, благодаря которой это можно исправить. если написать вот так:
то все выражение схлопнется, и образуется:
и это - кроссплатформенно. наверняка, человек который до такого додумался - настоящий ценитель! ("настоящими ценителями" я называю конченных извращенцев, если кто не понял) таким образом, предыдущую реализацию можно переписать вот так:
------------------------------------------- Часть 5. Перегрузка макросов. главная киллер-фича выше представленного способа: мы можем получать количество аргументов макроса времени препроцессирования. то бишь, получаемые циферки - это текст, который можно подмешивать при вызове других макросов. теперь мы готовы. суть идеи простая: допустим, у нас есть макрос:
мы уже умеем вычислять количество аргументов, переданное в макрос. из под главного макроса, просто сделаем вызов вложенного макроса-перегрузки подставив в его имя текст циферки - количество аргументов. а в качестве аргументов укажем __VA_ARGS__
я выделил общую одинаковую для разных макросов деталь. что бы максимум кода можно было вынести в библиотеку для повторного использования. от программиста требуется только описать сами перегрузки, трамплин для нуля аргументов, и префиксы имен перегрузок:
который послужил для меня первоисточником. http://stackoverflow.com/quest... h-c-macros на этом все. пока.
13
|
10.02.2016, 04:10 | |
Ответы с готовыми решениями:
17
[дизайн и эволюция] провалы в variadic конструкторы [Дизайн и эволюция] Дискриминация шаблона на примере макроса OUT_TO_STREAM Дизайн сайтов (desktop и адаптивный дизайн), баннеров и логотипов Дизайн выпадающего меню под дизайн обычного |
9 / 5 / 1
Регистрация: 15.08.2016
Сообщений: 48
|
|
15.10.2016, 13:41 | 2 |
Сами по себе макросы - большое зло, а тут еще их перегрузка
0
|
15.10.2016, 19:27 | 3 |
Тут нет перегрузки макросов. Хотя код действительно получается сложно читабельным. В основе лежит техника с более читаемым кодом. Но в этой технике приходится дополнительным параметром передавать количество параметров
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
|
22.06.2019, 01:38 | 4 | |||||
Интересная тема, но чтото большая слишком простыня получается
я вообщето любитель, извените что влажу со своими пирогами но позвольте озвучить свою задачу и может быть найти решение задача : перезагрузить 0-2 аргумента МИНИМУМ количества строк. выше предложенный метод конверт или есть еще CONCAT не нравиться своей длиной пока что я сделал 4 строками но без нулнвого
1) можно это сделать менее чем 4 строки макроса ? 2) как перегрузить с 0 аргументом в следующей форме FOO() -> ABC() ?
0
|
09.06.2020, 14:52 | 5 | |||||
Полное отсутствие гибкости, а что делать если в макрос нужно передавать код, например вызвать цикл где-то или объявить переменную или передать в макрос макрос что тогда.
Добавлено через 3 минуты Так что этот __VA_ARGS__ фигня полная умеет только цифры перебирать. И никакого толку от динамического количества аргументов нет.
0
|
8971 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
|
|||||||||||
09.06.2020, 18:14 [ТС] | 6 | ||||||||||
пример использования:
Код
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 на макросах.лично мне на практике пригодилась возможность реализовывать различные комбинаторные алгоритмы. например:
(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
|
09.06.2020, 18:30 | 7 |
Так что этот
__VA_ARGS__ фигня полная умеет только цифры перебирать.
А как сгенрировать мой текст чтобы выполнилось for(;;)break;b+=10; ваши примеры это игра с цифрами и текстом а я спрашиваю о том что я запихиваю код в макрос а нутри он по порядку весь выполянется. Это походу интерпретация тут наверное только Пион сможет такое, хотя что сложного в том выполнить по порядку аргументы макроса но видимо сложно. Получается что эффективность макросов с неограниченными параметрами нулевая.
0
|
8971 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
|
|
09.06.2020, 19:26 [ТС] | 8 |
твоя проблема в том, что ты пытаешься критиковать тему, в которой ни бум бум.
во-первых, с чего ты взял, будто бы __VA_ARGS__ вообще что-то там перебирает?вот откуда в твоей голове взялся этот бред? во-вторых, вот с чего ты взял, что __VA_ARGS__ можно составить только из цифр?такое впечатление, что ты вообще не понимаешь, что такое __VA_ARGS__ , и зачем он нужен. я думаю, тебе нужно открыть букварь, главу "препроцессор", и для начала изучить азы.
1
|
09.06.2020, 23:16 | 9 |
Я долго пытался найти что-то про этот элемент но как я посмотрю работает он только с цифрами и буквами или процедурными элементами спору нет вызвать функции он может по порядку, но я вас спрашиваю как вызвать этот код
for(;;)break;b+=10; , __VA_ARGS__ тут не катит получается что он и создан для перебора цифр и букв, все примеры включая ваш примитивны, возможно потому что сами макросы таковы примитивны поэтому и нельзя выполнить такую строку ACTION(for(;;)break;,z); вызов должен быть for(;;)break;b+=10; это и есть написание текстового кода в макросе да даже такой код оно не выполнит ACTION(;,;,;); , как только программа натыкается на символ ; между запятыми сразу же облом, вот так вот а писать цифры это да можно и в функции для этого макрос не нужен.Макросы это первое с чем можно работать потому что остальное на много сложнее а макрос некий интерпретатор.
0
|
8971 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
|
||||||
09.06.2020, 23:42 [ТС] | 10 | |||||
я ещё раз задам тебе вопрос:
https://rextester.com/HCOTY46347
Код
for for(;;)break;b+=10; void();,void();
0
|
10.06.2020, 01:52 | 11 |
Ну я так просто выразился. И это может быть не
__VA_ARGS__ , а какая-нибудь другая запись.
Значит возможность макросов с неограниченным количеством аргументов сводится только в выводе в консоль, меня в частности интересует выполнение аргумента а не вывод в консоль вот в чём моя идея, печатать в консоль можно и стихотворения, но меня интересует выполнения этого текста не в виде строки. Т.е. сначала запускается цикл for(;;)break; а потом происходит выход а потом выполняется этот код b+=10; ( при этом b может быть как объявлено так и нет int b+=10; ). Классная задумка да.
0
|
8971 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
|
|||||||||||
10.06.2020, 05:26 [ТС] | 12 | ||||||||||
то, что ты пишешь, или печатаешь в виде текста - это твои мысли.
если в тексте каша, значит в голове тоже каша. какая ещё такая "другая" запись? ты сам то хоть понимаешь о чем думаешь? нет, не сводится. с чего ты взял своё "значит" ? однозначно, ты не открывал букварь. и не изучал основы препроцессора. иначе как ещё можно объяснить тот факт, что ты до сих пор так и не понял, что препроцессор тупо подставляет обычный текст? https://rextester.com/AUP28595
Код
b = 20 (must be 20)
выложи человеческое словесное описание. выложи код, который не получается. но не нужно приходить в мою тему, и называть __VA_ARGS__ фигнёй только потому,что ты его не осилил.
0
|
Croessmah
|
10.06.2020, 11:24
#13
|
Не по теме: Она у него есть: Как использовать макросы с неограниченным количеством аргументов? Каюсь, отправил его сюда почитать...
0
|
10.06.2020, 11:39 | 14 | ||||||||||
А сделать такое сможет.
for(;;)break; , потом выполняется b+=10; у меня бывает нужда в таких конструкциях приходится по отдельности выполнять и цикл for(int q=-1;++q<3;) вызывать 2 раза, первый раз чтобы выполнить аргумент for(;;)break; , второй раз чтобы выполнить b+=10; . Мне нужно было сразу так объяснить.
0
|
Неэпический
|
|||||||||||
10.06.2020, 14:08 | 15 | ||||||||||
Так что ли?
P.S. Там косяк с ;
Добавлено через 20 минут Чуть "модернизировал". Кажется, работает и это как раз то, чего ты хочешь. Но это не точно.
https://wandbox.org/permlink/O3PgHnM1QhjxyteZ
0
|
10.06.2020, 15:17 | 16 | |||||
Да это то, чувствую что то.
Почему (UB) а зачем же эти языки программирования нужны. У меня есть программы где выбиваются предупреждения связанные с конвертацией типов, но либо новую переменную объявить либо предупреждение всё одно и тоже.
Также я удаляю макросы
{} #undef EXECUTE_AUX_10 #undef EXECUTE_AUX_9 А ещё плохо что приходится писать EXECUTE_AUX_10/9/8 константность какаета. Добавлено через 2 минуты И эти цифры блин 5,4,3,2,1,0 делают программу не универсальной, хреново это конечно. Как вариант прямо в коде удалить и переписать макрос и тогда получиться динамический макрос
0
|
Неэпический
|
|
10.06.2020, 16:28 | 17 |
UB здесь причем? Такую штуку можно использовать где-нибудь в недрах библиотеки, чтобы построить наверху что-то хорошее. Но использовать это наверху - это просто говнокод получится и всё.
Короче, фигня всё это. Ты просто хочешь не нормального. Не по теме: Я когда-то генерировал макросами функции-члены в которых макросами генерировались лямбды и, так сказать, на собственной шкуре понял, что это говнокод редкостный.
0
|
10.06.2020, 16:55 | 18 |
Ну так а что, я по возможности сокращаю код макрасами, но и макросы нужно делать как можно более универсальные чтобы выполнялось правило 3ёх и чтобы выражений включало это дело как можно больше. Ну тут да спору нет читабильность куда сильно ухудшается, но его же делаешь не для того чтобы читать, он либо на мусорку либо работает и лезть в налаженный механизм уже нет нужды. Спасибо за подсказки. Было бы ещё классно придумать что-то
#define EXECUTE_AUX_10/9/8 чтобы не писать все 10 штук и для функций это было бы классно. Ну тут по ходу уже ничего не сделаешь. Бывает даже функции пишешь int Int_1(){return 8;}Int_2()/3 и т.д. Вроде типа сделать чтобы всё было как массивы цифры.
0
|
10.06.2020, 16:55 | |
10.06.2020, 16:55 | |
Помогаю со студенческими работами здесь
18
Эволюция Эволюция растений Эволюция и человек Эволюция поверхности Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |