[preprocessor] use case: named parameters for macros

Hi, some of you might like this pattern - it might be even practical at places... Maybe it's new, at least I couldn't find anything about it on the web. Regards, Tobias // read bottom up // ---- "library" code #include <boost/preprocessor/comparison/equal.hpp> #include <boost/preprocessor/control/iif.hpp> #include <boost/preprocessor/tuple/elem.hpp> #include <boost/preprocessor/cat.hpp> // public #define ARG(i,args,deflt) \ BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_CAT(SEARCH_A_ ## i args (i,deflt),_END)) // private #define SEARCH_A_0(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(0,k),(k,v) SKIP_A, SEARCH_B_0) #define SEARCH_B_0(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(0,k),(k,v) SKIP_A, SEARCH_A_0) #define SEARCH_A_1(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(1,k),(k,v) SKIP_A, SEARCH_B_1) #define SEARCH_B_1(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(1,k),(k,v) SKIP_A, SEARCH_A_1) #define SEARCH_A_2(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(2,k),(k,v) SKIP_A, SEARCH_B_2) #define SEARCH_B_2(k,v) BOOST_PP_IIF(BOOST_PP_EQUAL(2,k),(k,v) SKIP_A, SEARCH_A_2) // ... #define SKIP_A(k,v) SKIP_B #define SKIP_B(k,v) SKIP_A #define SKIP_A_END #define SKIP_B_END // ---- macro with named parameters example // public #define MY_FIRST_ARG(value) (0,value) #define MY_SECOND_ARG(value) (1,value) #define MY_THIRD_ARG(value) (2,value) #define MY_MACRO(args) \ MY_MACRO_IMPL( ARG(0,args,1st_default), ARG(1,args,2nd_default), ARG(2,args,3rd_default) ) // private #define MY_MACRO_IMPL(arg1,arg2,arg3) \ arg1 arg2 arg3 // note: parameter names can be reused among macros // ---- client code MY_MACRO( MY_FIRST_ARG(yaba) MY_THIRD_ARG(doo) MY_SECOND_ARG(daba) ) // expands to: yaba daba doo MY_MACRO( MY_SECOND_ARG(bubu) ) // expands to: 1st_default bubu 3rd_default.

Hi, too funny I recently implemented named parameters for macros, too. But differently and specialised (no general-purpose framework or anything - not because it were not doable, but because I didn't need it). Nevertheless, they are used like this MACRO((foo, 77) (bar, int)) which will expand to say MACRO2(int, 77) The idea is "simple": 1. Generate a valid PP-Sequence from this "tuple-sequence"-thing 2. Generate a set of "modifications" by looking up the value of PARAM_ ## param (x), which will expand to (index, x) or something like this. 3. Apply those changes to an initial tuple. 4. Use the tuple to invoke another macro. I do not know which approach should be considered superior. But I really consider both interesting. I used my approach in a usable C++ library already, so the concept (which is the same for both) _is_ practical. Kind regards, Aristid PS: I do not think preprocessor metaprogramming should be over-used but there are a few places where it fits nicely. PPS: Quirks! Am Dienstag, den 24.10.2006, 22:24 +0200 schrieb Tobias Schwinger:
// ---- client code
MY_MACRO( MY_FIRST_ARG(yaba) MY_THIRD_ARG(doo) MY_SECOND_ARG(daba) ) // expands to: yaba daba doo
MY_MACRO( MY_SECOND_ARG(bubu) ) // expands to: 1st_default bubu 3rd_default.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hi, Aristid Breitkreuz wrote:
Hi,
too funny I recently implemented named parameters for macros, too. But differently and specialised (no general-purpose framework or anything - not because it were not doable, but because I didn't need it).
Well, the experiment was driven by the question whether it would be possible to add more features of spirit::rule to a macro-based replacement that uses typeof instead of virtual dispatch (see boost/spirit/utility/rule_parser.hpp, note: no named parameters in there, yet) without compromising the interface or even making it better.
Nevertheless, they are used like this
MACRO((foo, 77) (bar, int))
which will expand to say
MACRO2(int, 77)
Nice! I like this kind of interface, because parameter names do not have to be full-blown macros (which will need a potentially verbose disambiguation prefix in real life). I think, I'll adopt it.
The idea is "simple": 1. Generate a valid PP-Sequence from this "tuple-sequence"-thing 2. Generate a set of "modifications" by looking up the value of PARAM_ ## param (x), which will expand to (index, x) or something like this. 3. Apply those changes to an initial tuple. 4. Use the tuple to invoke another macro.
Interesting. That approach would allow a usage like #define MY_MACRO(args) \ NAMED_PARAMS_MACRO(MY_MACRO_IMPL,(bar,1st_default)(foo,2nd_default), args) which I happen to like. I think I'll adopt this one, too (although I'm not sure about the implementation).
I do not know which approach should be considered superior. But I really consider both interesting.
Indeed!
I used my approach in a usable C++ library already, so the concept (which is the same for both) _is_ practical.
Kind regards,
Aristid
PS: I do not think preprocessor metaprogramming should be over-used but there are a few places where it fits nicely.
Here's my rule of thumb on this issue: If you have the choice between the preprocessor and another language feature, prefer the other language feature, unless it really hurts to do so. If you have the choice between the preprocessor and an external code generator, prefer the preprocessor unless it really hurts to do so. Thanks a lot for the inspiration. Regards, Tobias

On 10/25/2006 05:30 AM, Tobias Schwinger wrote: [snip]
Well, the experiment was driven by the question whether it would be possible to add more features of spirit::rule to a macro-based replacement that uses typeof instead of virtual dispatch (see boost/spirit/utility/rule_parser.hpp, note: no named parameters in there, yet) without compromising the interface or even making it better.
The zip file: <boost-vault>/Template Metaprogramming/proto_static_disp.zip uses g++ typeof, CRTP, and mpl::fold to do what sounds like something similar, i.e. eliminate the virtual dispatch. IIRC, the CRTP was needed since the parse functions were mutually recursive and CRTP allowed them to be defined in the same class, or something like that. Maybe viewing your code would give me some ideas about how to tweak proto_static_disp. Any chance you could post it?

Hi Larry, Larry Evans wrote:
The zip file:
<boost-vault>/Template Metaprogramming/proto_static_disp.zip
uses g++ typeof, CRTP, and mpl::fold to do what sounds like something similar, i.e. eliminate the virtual dispatch. IIRC, the CRTP was needed since the parse functions were mutually recursive and CRTP allowed them to be defined in the same class, or something like that.
Sounds interesting! Is there some kind of documentation? If not, would you mind adding some detail to your description?
Maybe viewing your code would give me some ideas about how to tweak proto_static_disp. Any chance you could post it?
It can be found in the Boost CVS (see path in my previous post). There is no offical documentation, but there is a huge comment block in the header. Plus sample code in libs/spirit/techniques/no_rules_with_typeof. I was planning on writing real docs (for the upcoming 1.8.5 release, given I finish in time), but if I want improve the interface I better do it first. Regards, Tobias

On 10/25/2006 07:38 AM, Larry Evans wrote:
On 10/25/2006 05:30 AM, Tobias Schwinger wrote: [snip]
replacement that uses typeof instead of virtual dispatch (see [snip] The zip file:
<boost-vault>/Template Metaprogramming/proto_static_disp.zip
uses g++ typeof, CRTP, and mpl::fold to do what sounds like something similar, i.e. eliminate the virtual dispatch. IIRC, the CRTP was In a private email to me, Tobias kindly pointed out that the method in proto_static_disp has similarities with spirit's subrule method for eliminating virtual dispatch. I should have researched spirit more thoroughly and noted this similarity in my vault code and the post to this list.

Am Mittwoch, den 25.10.2006, 12:30 +0200 schrieb Tobias Schwinger:
Hi,
Hi, for short: I hereby provide a sample implementation with (possibly) room for optimization and sub-optimal macro names. Consider it a proof of concept and further inspiration. If there is demand, I can license it permissively.
[snip] Well, the experiment was driven by the question whether it would be possible to add more features of spirit::rule to a macro-based replacement that uses typeof instead of virtual dispatch (see boost/spirit/utility/rule_parser.hpp, note: no named parameters in there, yet) without compromising the interface or even making it better.
Oh. Virtual dispatch is still necessary for recursion, right? This is surely a complex topic.
Nevertheless, they are used like this
MACRO((foo, 77) (bar, int))
which will expand to say
MACRO2(int, 77)
Nice! I like this kind of interface, because parameter names do not have to be full-blown macros (which will need a potentially verbose disambiguation prefix in real life). I think, I'll adopt it.
Yeah that's something I recognized while implementing it :D.
[snip]
Interesting. That approach would allow a usage like
#define MY_MACRO(args) \ NAMED_PARAMS_MACRO(MY_MACRO_IMPL,(bar,1st_default)(foo,2nd_default), args)
which I happen to like. I think I'll adopt this one, too (although I'm not sure about the implementation).
An adaptation of which I hereby provide.
[snip]
PS: I do not think preprocessor metaprogramming should be over-used
but
there are a few places where it fits nicely.
Here's my rule of thumb on this issue:
If you have the choice between the preprocessor and another language feature, prefer the other language feature, unless it really hurts to do so. If you have the choice between the preprocessor and an external code generator, prefer the preprocessor unless it really hurts to do so.
That's what I already did intuitively so I'm fine.
Thanks a lot for the inspiration.
Regards,
Tobias
Regards, Aristid

Aristid Breitkreuz wrote:
[snip] Well, the experiment was driven by the question whether it would be possible to add more features of spirit::rule to a macro-based replacement that uses typeof instead of virtual dispatch (see boost/spirit/utility/rule_parser.hpp, note: no named parameters in there, yet) without compromising the interface or even making it better.
Oh. Virtual dispatch is still necessary for recursion, right?
No. Of course you'll need a static call to recurse and a dynamic one to return at /some/ point (since the compiler can't inline forever), but you do not need virtual dispatch, which involves at least two dynamic calls (including the return) plus the vtable lookup. It works for the same reason CRTP does -- the laziness of template instantiation makes it possible. Here is some code that attempts to illustrate how: template<class X> class A { X & ref_that; public: explicit A(X & that) : ref_that(that) { } void do_() { ref_that.do_(); } }; class B { A<B> obj_buddy; public: B() : obj_buddy(*this) { } void do_() { obj_buddy.do_(); } }; 'A<B>::do_' and 'B::do_' recurse back and forth. Spirit's subrule template also works without virtual dispatch (however, the setup happens expression level). Regards, Tobias

Aristid Breitkreuz wrote:
Hi,
for short: I hereby provide a sample implementation with (possibly) room for optimization and sub-optimal macro names. Consider it a proof of concept and further inspiration.
If there is demand, I can license it permissively.
Does the Boost license work? Your work will be acknowledged, of course. <code> Very nice! Quite expansion-heavy, but it isn't problematic according to my benchmarks. Thanks, Tobias

Am Freitag, den 27.10.2006, 00:40 +0200 schrieb Tobias Schwinger:
Does the Boost license work? Your work will be acknowledged, of course.
Yeah it's fine (just read it). Appended a version with the proposed notice.
<code>
Very nice! Quite expansion-heavy, but it isn't problematic according to my benchmarks.
Thanks :). I would be glad to see the improvements you or anybody wishes to make. Of course you don't _need_ to show them.
Thanks,
Tobias
Greetings, Aristid PS: Wondering whether I should propose that for Boost.

Aristid Breitkreuz wrote:
I would be glad to see the improvements you or anybody wishes to make. Of course you don't _need_ to show them.
Just implemented tranformation of the previous argument value for the same parameter to e.g. allow certain parameters to be specified multiple times to form a sequential data structure. I got away with a single fold plus the "tupleizing" step for the binary sequence. The code's in the vault: http://tinyurl.com/yk8z3u
PS: Wondering whether I should propose that for Boost.
I'd sure welcome such a utility header! However, it could be a good idea to start a new thread with an explicit question in the subject to find out whether there are more people interested. Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:ehlsqa$bbt$1@sea.gmane.org...
Hi,
some of you might like this pattern - it might be even practical at places... Maybe it's new, at least I couldn't find anything about it on the web.
check this crap out: http://groups.google.com/group/comp.lang.c++.moderated/msg/1aae5ddf7dbd5527 :O

Chris Thomasson wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:ehlsqa$bbt$1@sea.gmane.org...
Hi,
some of you might like this pattern - it might be even practical at places... Maybe it's new, at least I couldn't find anything about it on the web.
check this crap out:
http://groups.google.com/group/comp.lang.c++.moderated/msg/1aae5ddf7dbd5527
I'll give it a read once I have some time in one piece. But it seems I have to make one thing clear: I wasn't talking about the implementation (the techniques I used are *not* new at all) -- I was talking about having named parameters for macros (I'm just applying some of the "black magic" spread by Paul Mensonides to real world use cases)! If you are interested in pushing the computational limits of the preprocessor you really should have a look at Paul's library (if you don't know it aleady ;-). http://sourceforge.net/projects/chaos-pp Chaos provides a framework to write fully reentrant, generic algorithms within the preprocessor, which is pretty amazing IMO. Regards, Tobias
participants (4)
-
Aristid Breitkreuz
-
Chris Thomasson
-
Larry Evans
-
Tobias Schwinger