12/3/2016
Variadicmacrostricks|Codecraft
Codecraft
software=science+art+people
Variadicmacrostricks
Haveyoueverwantedtowriteaforeachloopoveralltheargsofa
variadicmacro?Orhaveyoueverwantedtooverloadamacroonthe
numberofarguments?(Ifyouresayingtoyourself,goodgrief,why?
Illdescribeausecaseattheboomofthispost.)
Ilearnedhowtodothistoday,andIwantedtoblogaboutittocement
thetechniqueinmyownmind.
(hp://[Link]/1319/)
[Link]
spareyou:)Imagecredit:[Link]
[Link]
1/7
12/3/2016
Variadicmacrostricks|Codecraft
Simplevariadicmacros
Therstpieceofmagicyouneedtodosomethinglikethisis
__VA_ARGS__.Thisallowsyoutowritemacrosthattakeanarbitrary
numberofarguments,using...torepresentthemacrosparameters:
1 #deneeprintf(fmt,...)\
2 fprintf(stderr,fmt,__VA_ARGS__)
viewrawvariadic_macro_1hostedwith byGitHub
Nice.__VA_ARGS__isastandardfeatureofC99,andIveknownaboutit
[Link](andClangs)extension,
whichaachesspecialmeaningto##__VA_ARGS__ifitsprecededbya
commaitremovesthecommaif##__VA_ARGS__expandstonothing.IfI
changemymacrodenitionto:
1 #deneeprintf(fmt,...)\
2 fprintf(stderr,fmt,##__VA_ARGS)
viewrawfancier_variadic_macrohostedwith
byGitHub
Icannowcalleprintf("hello,world");withoutacomplaintfrom
thecompiler.
Butitsnotenough
Thatdoesntletmedoaforeachloop,[Link]
areexpanded,butIcantdoanythingwiththem,[Link]
namesformymacrosparametersjusttheanonymous.
Iwentpokingaround,notexpectingtondasolution,butIwas
pleasantlysurprised.
Thepaired,slidingarglisttrick
Thenextbuildingblockweneedisatechniquethatusestwo
complementarymacrosplus__VA_ARGS__toselectsomethingspecic
[Link]
[Link](hp://[Link]/a/11763277),andyoucan
parseitalloutdirectlyfromthere,[Link]
anexplanationthattakesitonestepatatime:
[Link]
2/7
12/3/2016
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Variadicmacrostricks|Codecraft
//Acceptanynumberofargs>=N,[Link],
//[Link]
//[Link]
//anunderscoreitsanimplementationdetail,notsomethingweexpectpeople
//tocalldirectly.
#dene_GET_NTH_ARG(_1,_2,_3,_4,N,...)N
//Counthowmanyargsareinavariadicmacro.OnlyworksforuptoN1args.
#deneCOUNT_VARARGS(...)_GET_NTH_ARG(__VA_ARGS__,4,3,2,1)
intmain(){
printf(onearg:%d\n,COUNT_VARARGS(1));
printf(threeargs:%d\n,COUNT_VARARGS(1,2,3));
}
//output
onearg:1
threeargs:3
viewrawpaired_sliding_arg_list_macro_trick_1hostedwith
GitHub
by
Seehowitworks?Therstmacro,_GET_NTH_ARG(),takesanynumber
ofargs>=N,butalwaysreturnsitemN(inthiscase,N=5).Thesecond
macro,COUNT_VARARGS(...),takesanarbitrarynumberofargs<N,
padswithcandidatevaluesitwantstoextract,andusesitsargstocall
_GET_NTH_ARG()inawaythatputstherightcandidatevalueinthe
[Link],themeaningfulpieceofinfothatwewant
inpositionNisanargcount;weveprovidedthevalues4,3,2,1as
candidatevalues,andoneofthosevalueswillbeinpositionNon
expansion.
TweakingthismacropairtohandleadierentNisamaerofadjusting
whatcomesbeforeNintherstmacro,andwhatcomesafter
__VA_ARGS__inthesecondmacro.Illleavethatasanexerciseforthe
reader.:)
Wedonthavetoselectanumericcountwiththistechnique;wecould
useittoselectargnameswiththe#operator,[Link]
[Link],letsaddressoneshortcoming:
COUNT_VARARGS(...)[Link]
x:
1
2
3
//Acceptanynumberofargs>=N,[Link]
//thatcallsusstillonlysupports4args,butthesetofvalueswemight
//needtoreturnis1larger,soweincreaseNto6.
[Link]
3/7
12/3/2016
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Variadicmacrostricks|Codecraft
#dene_GET_NTH_ARG(_1,_2,_3,_4,_5,N,...)N
//[Link]/Clangsextensionto
//handlethecasewhere...[Link]
//##__VA_ARGS__(itsvalueistotallyirrelevant,butitsnecessarytopreserve
//theshiftingosetwewant).Inaddition,wemustadd0asavalidvaluetobein
//theNposition.
#deneCOUNT_VARARGS(...)_GET_NTH_ARG(ignored,##__VA_ARGS__,4,3,2,1,0)
intmain(){
printf(zeroargs:%d\n,COUNT_VARARGS());
printf(threeargs:%d\n,COUNT_VARARGS(1,2,3));
}
//output
zeroargs:0
threeargs:3
viewrawpaired_sliding_arg_list_macro_trick_2hostedwith
GitHub
by
Macrooverrides
Now,wecanbuildonthistodeneavariadicmacrothathasan
[Link]
[Link]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Denetwooverridesthatcanbeusedbytheexpansionof
//ourmainmacro.
#dene_MY_CONCAT3(a,b,c)abc
#dene_MY_CONCAT2(a,b)ab
//Deneamacrothatusesthepaired,slidingarglist
//[Link]
//recognizethisassimilartotheGET_NTH_ARG()macroin
//previousexamples.
#dene_GET_OVERRIDE(_1,_2,_3,NAME,...)NAME
//Deneamacrothatconcatseither3or2stringstogether.
#deneMY_CONCAT(...)_GET_OVERRIDE(__VA_ARGS__,\
_MY_CONCAT3,_MY_CONCAT2)(__VA_ARGS__)
[Link]
4/7
12/3/2016
Variadicmacrostricks|Codecraft
15
16 intmain(){
17 printf(3args:%s\n,MY_CONCAT(a,b,c));
18 printf(2args:%s,MY_CONCAT(a,b));
19 }
20
21 //output
22 3args:abc
23 2args:ab
viewrawmacros_overridden_by_arg_counthostedwith byGitHub
Nowweregeingclosetobeingabletocodeaforeachloopoverall
[Link]
eachavor,itallcomestogether:
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
//Acceptanynumberofargs>=N,butexpandtojusttheNthone.
//Here,N==6.
#dene_GET_NTH_ARG(_1,_2,_3,_4,_5,N,...)N
//Denesomemacrostohelpuscreateoverridesbasedonthe
//arityofaforeachstylemacro.
#dene_fe_0(_call,...)
#dene_fe_1(_call,x)_call(x)
#dene_fe_2(_call,x,...)_call(x)_fe_1(_call,__VA_ARGS__)
#dene_fe_3(_call,x,...)_call(x)_fe_2(_call,__VA_ARGS__)
#dene_fe_4(_call,x,...)_call(x)_fe_3(_call,__VA_ARGS__)
/**
*[Link]
*to4args.
*
*Exampleusage1:
*#deneFWD_DECLARE_CLASS(cls)classcls;
*CALL_MACRO_X_FOR_EACH(FWD_DECLARE_CLASS,Foo,Bar)
*
*Exampleusage2:
*#deneSTART_NS(ns)namespacens{
*#deneEND_NS(ns)}
*#deneMY_NAMESPACESSystem,Net,Hp
*CALL_MACRO_X_FOR_EACH(START_NS,MY_NAMESPACES)
*typedeffooint;
*CALL_MACRO_X_FOR_EACH(END_NS,MY_NAMESPACES)
*/
[Link]
5/7
12/3/2016
29
30
31
Variadicmacrostricks|Codecraft
#deneCALL_MACRO_X_FOR_EACH(x,...)\
_GET_NTH_ARG(ignored,##__VA_ARGS__,\
_fe_4,_fe_3,_fe_2,_fe_1,_fe_0)(x,##__VA_ARGS__)
viewrawfor_each_macrohostedwith
byGitHub
Okay,butwhy?
IsaidIdprovidesomeexplanationofwhythistechniquecouldbe
[Link],Iamnotafanofmacrosrewritingthesyntaxofa
programminglanguage;thatcanobscurewhatsreallyhappening,and
makeforasteeperlearningcurve.
Ontheotherhand,[Link]
codemuchlessverbose/repetitivebyeliminatingnoiseandboilerplate.
Occasionally,Irunintocaseswherethattradeoseemsworthittome.
Moreimportantly,macroshaveapropertythatyoucantgetanyother
waythesamefragmentofcodecanhavemultiplemeanings,andcan
maintainthissemanticparallelismwithoutbeingsusceptibletohuman
memoryerrors,laziness,[Link]
bloggedabouthowvaluablethiscanbeineliminatingencapuslation
problemswithenums(hps://[Link]/2012/10/29/howenums
spreaddiseaseandhowtocureit/),butIrecentlyfoundanotherneed
[Link]
(hps://[Link]/2013/10/24/onbreadrecipesmapsandintentions/),
Ihavetocreatesomefoundationpackagesandclassestheanalogto
[Link],[Link]
wrieninC/C++[Link]
somewaytousenamespaces,classes,andotherC++constructsinthe
sourcecode,butalsogeneratepackageandclassconstructsvisibletomy
[Link].
Theonlyproblemwasthatsomeofmymacrosneededtobevariadic
[Link].:)
Howaboutyou?Haveyoueverhadaneedforsomethinglikethis?
TUE,NOV25,2014
DANIELHARDMAN
MACROS,NAMESPACES,TRICKS,VARIADIC,__VA_ARGS__
2thoughtsonVariadicmacrostricks
[Link]
1.
JasonIveysays:
6/7
12/3/2016
Variadicmacrostricks|Codecraft
1.
JasonIveysays:
[Link]
macrosgetabadnamethesedays,thepreprocessoritselfisstilla
powerfulandwonderfultoolwhenusedfortheproblemsyou
described.
WhatIvediscoveredrecentlyasIhavebeenwritingcustommacros
isthatmany,ifnotall,oftheunderlyingcodeIinventisalready
[Link]
identicalsolutiontowhatyouhavecreatedabovebutIknowithasa
macrotoconvertthevar_argstoacountandlist.
(BOOST_PP_VARIADIC_TO_LIST)
Iwasalsopleasedtodiscoverthattheyhavethemechanicstoquickly
implementmyfavoritepreprocessorpaernyoutaughtmeyears
ago,theenumdeclarationviaincludele.(BOOST_PP_ITERATION)
Inmyopinion,[Link]
[Link]
alottheretoworkwith.
REPLY MON,JAN5,2015AT6:23PM
DanielHardmansays:
Jason:[Link],butIhavent
[Link]!Thanksforremindingmetolearn
aboutit.
WhenIrunintoaprogrammingproblemthatIdontknowhow
tosolve,Ioftenliketowritemyownsolutionnotsomuch
becauseIwantto*use*myownsolution,asbecauseIwantto
[Link]
ownsatisfaction(and,sometimes,wrienaboutitsoIunderstand
howitworkswell),thenIcanappreciateamoreelegantor
generalsolution,[Link]
[Link]
theintentcodebase;ifso,Illgladlyswitchover,sinceImalready
usingboostafairamount.
REPLY MON,JAN5,2015AT6:57PM
[Link].
[Link]
7/7