[fusion][parameter] a solution for named parameters / parameter packs using fusion maps

Hello, I just realized that named parameters can be implemented rather nicely using fusion maps (I'm not sure whether this has been explored before). I threw together an implementation for two particular parameters/names, but the method could be easily generalized. Here are examples of functions taking parameter packs (label is a string, and size is a guigl::size_type which is a gil::point2 type) (...and don't worry about what guigl is): using namespace guigl::parameter; const std::string &function_taking_label(const pack<field::label>::type &args) { return args.label(); } const guigl::size_type &function_taking_size(const pack<field::size>::type &args) { return args.size(); } const guigl::size_type &function_taking_both(const pack<field::label, field::size>::type &args) { return args.size(); } Now you can do things like: function_taking_label(label("hello")); const guigl::size_type size_one(1,1); function_taking_size(size(size_one)); // parameters can be passed in any order function_taking_both(label("hello").size(size_one)); function_taking_both(size(size_one).label("hello")); // size has a default value function_taking_both(label("hello")); // extraneous parameters will be ignored function_taking_label(label("hello").size(size_one)); // empty parameter pack can be used for no parameters (all default values) function_taking_size(empty()); Here is the full test code: http://pastebin.com/f6b128a4b and the relevant implementation: http://pastebin.com/f584c7967 Compared to Boost.Parameter, I have a hunch that the Fusion approach would be faster to compile (haven't tested that though), and is a little bit less convoluted (esp. for certain cases like constructors using named parameters). I've never used Boost.Parameter so I'm not sure what other comparisons might apply. Any feedback welcome. Kind regards, Stjepan

<snip> This looks neat and feels C++-ish (rather than IBM JCL - but you're too young to remember that ;-) Jake Voytko and I concluded similarly when developing Scale Vector Graphics PLotting user interface where there are hundreds of possible functions (x_axis_color(red), point_color(green)...). (Watch this space for a enhanced and possibly useable version). Chaining calls is convenient because you don't need to remember the order. The compile time seemed a little better too compared to Boost.Parameter, and the setup more intuitive (C++y rather than macros). Since it now clear that named parameters are not going to in the C++ language proper, I believe that this is best idiom to use. Despite being unfamiliar, chaining is obvious once you get going with it. Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

On Wed, Aug 13, 2008 at 8:09 AM, Paul A Bristow <pbristow@hetp.u-net.com> wrote:
This looks neat and feels C++-ish (rather than IBM JCL - but you're too young to remember that ;-)
I'm apparently too young to even know what it is :-)
Jake Voytko and I concluded similarly when developing Scale Vector Graphics PLotting user interface where there are hundreds of possible functions (x_axis_color(red), point_color(green)...).
I remember following that discussion.
As of right now, I have something even better :-) How about this syntax: function_taking_both(( label("hello"), size(1) )); or, if double parens aren't your thing, how about this: function_taking_both( label("hello") & size(1) ); Even better than the even better syntax, the new implementation completely separates the fields from the "library". You now do this to define the fields: #include <boost/guigl/parameter_pack.hpp> namespace field { struct label { typedef std::string type; }; struct size { typedef int type; static type default_value() { return 3; } }; } BOOST_PARAMETER_FUNCTION(field::label, label) BOOST_PARAMETER_FUNCTION(field::size, size) and you are good to go!!! New implementation: http://pastebin.com/f29304ed New test: http://pastebin.com/f60120d0d I need to fix default construction, and extend the number of possible fields (real quick fixes), but other than that, this should be pretty usable (minus ironing out little kinks which are to be expected until it gets used a bit). Cheers! Stjepan PS I hope I can fall asleep now. I woke up at 4am with this idea in my head, and couldn't get back to sleep. So I got up and implemented it. At 7am I tried going back to bed, but unfortunately, the idea for the better solution struck me and I still couldn't fall asleep. So I got back up and implemented that. Perhaps now...

Stjepan Rajko:
function_taking_both( label("hello") & size(1) );
FWIW, I've been using this in a context where I needed "arbitrary" argument lists and it works. It has the advantage that the caller only needs to know about label and size, the eventual callee only needs to know about the arguments it recognizes and can ignore the rest, and the intermediate layers can pass everything downstream as-is without knowing about anything.

on Wed Aug 13 2008, "Peter Dimov" <pdimov-AT-pdimov.com> wrote:
Why is everyone hand-rolling the functionality we already have in Boost.Parameter? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

on Wed Aug 13 2008, "Peter Dimov" <pdimov-AT-pdimov.com> wrote:
Whew; that makes me feel a *little* better, anyway :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Wed, Aug 13, 2008 at 11:41 AM, David Abrahams <dave@boostpro.com> wrote:
I can give you my reasons, at least :-) The major deciding factor was that I thought that Boost.Parameter-ized constructors required a base class to dispatch to. Looking back at the Boost.Parameters docs, here is what gave me that impression (section 2.3): "The lack of a "delegating constructor" feature in C++ (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf) limits somewhat the quality of interface this library can provide for defining parameter-enabled constructors. The usual workaround for a lack of constructor delegation applies: one must factor the common logic into a base class." Note the word "must". Reading that entire section, I found nothing to change that impression. Another reason is that I found the following syntax intimidating: BOOST_PARAMETER_FUNCTION( (void), // 1. parenthesized return type depth_first_search, // 2. name of the function template tag, // 3. namespace of tag types (required (graph, *) ) // 4. one required parameter, and (optional // four optional parameters, with defaults (visitor, *, boost::dfs_visitor<>()) (root_vertex, *, *vertices(graph).first) (index_map, *, get(boost::vertex_index,graph)) (in_out(color_map), *, default_color_map(num_vertices(graph), index_map) ) ) ) It looks like a very powerful syntax, but also something that would be easy to trip over (remembering the order of macro parameters, for example). Perhaps it is easy to just write what the macro would expand to by hand, but after deciding not to use the library because of the first reason I listed, I didn't really feel motivated to look into it further. Finally, I was concerned about the impact on compile time, which I've seen mentioned on the list a few times. Whether what I have now is any faster is yet to be tested. Stjepan

on Wed Aug 13 2008, "Stjepan Rajko" <stipe-AT-asu.edu> wrote:
You have to read on to the "Advanced Usage" section: http://www.boost.org/doc/libs/1_35_0/libs/parameter/doc/html/index.html#more... to see that you can build ArgumentPacks directly. However, although that may be simpler from the library implementor's point-of-view, it imposes several important limitations on your clients. First and foremost is that positional arguments become much harder for them to use. I wrote the tutorial with the assumption that the reader was primarily interested in providing the best possible interface to his clients.
Actually that's very easy. With the exception of "tag", the order corresponds to exactly what you'd write if named parameters were supported in the language: template <class Graph, class Visitor, class IndexMap, class ColorMap> void depth_first_search // tag ( // required Graph const& graph, // optional Visitor const& visitor = boost::dfs_visitor<>(), typename graph_traits<Graph>::vertex_descriptor root_vertex = *vertices(graph).first IndexMap const& index_map = get(boost::vertex_index, graph), ColorMap& c = default_color_map(num_vertices(graph), index_map) );
Perhaps it is easy to just write what the macro would expand to by hand,
Of course it isn't; if it were easy we wouldn't have felt compelled to provide a macro! What the macro does works so well for the library author *and* his clients that we absolutely had to provide it. The macro handles the forwarding problem, uses SFINAE to ensure the function template doesn't match invalid arguments, and unwinds the ArgumentPack so that both the types and values of the parameters are conveniently available *by name* in the function body. It allows arguments to be passed both positionally and by name (not to mention support for deduced parameters that don't need to be named).
I'd be interested in hearing the results. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG David Abrahams wrote:
Boost.Paramter seems to instantiate a lot of templates. mpl::if_ is instantiated over 600 times by the test preprocessor_deduced.cpp which makes 18 calls to BOOST_PARAMETER_FUNCTIONs In Christ, Steven Watanabe

on Wed Aug 13 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
Interesting. How do you come up with a statistic like that? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG David Abrahams wrote:
I used this http://svn.boost.org/svn/boost/sandbox/tools/profile_templates/ At the moment, it will only work with msvc. In Christ, Steven Watanabe

On Wed, Aug 13, 2008 at 1:51 PM, David Abrahams <dave@boostpro.com> wrote:
OK I felt motivated to get some results :-) I tested on the following use case: class named_constructableN { public: // a macro could extend the number of parameters if desired template<typename FM0, typename FM1, typename FM2> named_constructableN(const FM0 &a0, const FM1 &a1, const FM2 &a2) { label = (a0, a1, a2)[labelN]; size = (a0, a1, a2)[sizeN]; volume = (a0, a1, a2)[volumeN]; } std::string label; int size; double volume; }; //... named_constructableN constructed(labelN = "hello", sizeN = 1, volumeN = 1.0); BOOST_CHECK_EQUAL(constructed.label, "hello"); BOOST_CHECK_EQUAL(constructed.size, 1); BOOST_CHECK_EQUAL(constructed.volume, 1.0); I put together three versions of the test - one using the fusion based solution (I'm calling it field_map), one using the same use code but using Boost.Parameter, and then one using the recommended Boost.Parameter method for constructors. I then varied the number of "replicas" of the above code for the test. The following times were reported for 2 replicas in each test case: user: [test_field_map_compilation.o] 0.000249 system: [test_field_map_compilation.o] 0.000028 user: [test_parameter_compilation.o] 0.000102 system: [test_parameter_compilation.o] 0.000013 user: [test_parameter_dispatch_compilation.o] 0.000135 system: [test_parameter_dispatch_compilation.o] 0.000015 And the following for 20 replicas: user: [test_field_map_compilation.o] 0.002128 system: [test_field_map_compilation.o] 0.000139 user: [test_parameter_compilation.o] 0.000195 system: [test_parameter_compilation.o] 0.000030 user: [test_parameter_dispatch_compilation.o] 0.000555 system: [test_parameter_dispatch_compilation.o] 0.000041 I committed the tests to the sandbox, if you're interested in the details (I'm not sure whether I designed the tests adequately): http://svn.boost.org/svn/boost/sandbox/guigl/libs/guigl/test/ So, to revisit my speculations, these results would indicate that the fusion-based approach (as I have it implemented, at least) is significantly slower (in terms of compile-time) than using Boost.Parameter with the same syntax. Since it turned out that Boost.Parameter supports that same syntax, the fusion-based approach also isn't less convoluted, as I suggested it might be. Of course, perhaps I am again being hasty :-) Kind regards, Stjepan

On Wed, Aug 13, 2008 at 1:25 PM, Peter Dimov <pdimov@pdimov.com> wrote:
I do something like that with Boost.Parameter in a GUI library. A third-party component can create keywords and use them in the create<> function template, which gives very interface uniformity to the client. -- Felipe Magno de Almeida

I'm apparently too young to even know what it is :-)
LOL! Lucky you! It was something one had to use for IBM Job Control on punched cards, or even paper tape. It used named parameters in its syntax and to a spotty youth it seemed unscrutable and most unhelpful - a comma wrong and you lost your run for the day. But actually it was rather a lot simpler than C++ Macros and templates. Now I'm really showing my age :-(( Paul PS Both of these methods of providing parameters work. I don't want to make a big issue about which is best. My preference is probably mostly based on ignorance and NIH. --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

on Wed Aug 13 2008, "Paul A Bristow" <pbristow-AT-hetp.u-net.com> wrote:
You're free to use a "C++-y setup rather than macros" with Boost.Parameter if you're willing to present your users with a slightly less natural interface. The point of the macros is that 1. they give your *clients* a natural interface, and 2. they make your function body more natural to write (no need to use traits to access the types of the actual arguments nor any need to write fiddly function calls to get at the actual argument values
And it has not-so-obvious downsides. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

on Wed Aug 13 2008, "Stjepan Rajko" <stipe-AT-asu.edu> wrote:
Yes, we looked into it. It didn't work out for various reasons.
Yes, but this chaining interface is the same one used by the old Boost.Graph named parameter interface (which incidentally I suggested), and has a nasty coupling problem that we explicitly wanted to avoid: all the possible parameter names need to be known in one place, and must be available to all the functions that may use said parameters. I suggest going back in the message archives to look at the original review commentary for Boost.Parameter if you want to understand our design rationale. Hmm, I suppose we really ought to document that.
Why don't you do some testing if you think so? Don't forget to make sure your library supports all the same features as Boost.Parameter. Making it pass the Boost.Parameter test suite would be a good start.
and is a little bit less convoluted (esp. for certain cases like constructors using named parameters).
Boost.Parameter supports an equivalent approach to the one you're using here, if you want to use it, which avoids the need for a base class to dispatch to. The problem is that, like your approach, the syntax is slightly unnatural: SomeClass((label="hello", size=size_one)) In other words, you can explicitly build an ArgumentPack and pass that.
I've never used Boost.Parameter so I'm not sure what other comparisons might apply.
Then isn't it a bit early to be talking about the superiority of this approach? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Wed, Aug 13, 2008 at 8:43 AM, David Abrahams <dave@boostpro.com> wrote:
I fixed that problem (see my recent reply to Paul Bristow and referenced code snippets therein).
Testing is a good suggestion, but I don't have the time right now to do that :-) I'm not necessarily interested in replicating all Boost.Parameter features. I'm quite happy with the fusion-based solution as a simple one.
Oh, cool, I didn't know that. In my recent scan through Boost.Parameter docs I only noticed the approach that required the base class to dispatch to. It was only a quick scan though.
Seems like a reasonable syntax. BTW, I just extended the fusion-based solution to also support that syntax.
I didn't claim superiority - I only gave indications of two things where I think it might have an advantage. The source code is approx 140 lines of straightforward use of fusion, and no preprocessor magic (I leave that to fusion :-)) - also, the generic version mentioned in my reply to Paul Bristow makes it very easy to add new fields. Granted, I haven't taken a look at Boost.Parameter source code, and my "knowledge" of its used is based on memory from reading the docs and various discussions on the list where people have commented on it. Thanks for taking a look, Stjepan

on Wed Aug 13 2008, "Stjepan Rajko" <stipe-AT-asu.edu> wrote:
Okay, so what are you trying to accomplish?
It's just the same syntax you use normally, but with an extra set of parentheses.
BTW, I just extended the fusion-based solution to also support that syntax.
Well, at the time we redesigned the library, I encouraged Daniel Wallin to look into whether we could gain anything by using fusion, because the fundamental data structure we use is like a fusion map. He said we couldn't gain anything significant, and at the time I took his word for it. The entire code corresponding to fusion::map is contained in boost/parameter/aux_/arg_list.hpp. The rest of the library consists primarily of functionality you're not providing.
The latter is already the case in Boost.Parameter.
I'm just saying, if you're going to make comparisons of the two libraries, they should be informed comparisons and not speculation. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Wed, Aug 13, 2008 at 11:54 AM, David Abrahams <dave@boostpro.com> wrote:
Being able to have named parameters and not using Boost.Parameter.
class named_constructable { public: // a macro could extend the number of parameters if desired template<typename FM0, typename FM1> named_constructable(const FM0 &a0, const FM1 &a1) { // this could be done lazily... map<field::label, field::size>::type args = a0 & a1; label = args.at<field::label>(); size = args.at<field::size>(); } std::string label; int size; }; BOOST_AUTO_TEST_CASE( test_multi_param_construction ) { named_constructable constructed(label_ = "hello", size_ = 1); BOOST_CHECK_EQUAL(constructed.label, "hello"); BOOST_CHECK_EQUAL(constructed.size, 1); } If our approaches are similar, that would suggest Boost.Parameter could do the same.
I'm just saying, if you're going to make comparisons of the two libraries, they should be informed comparisons and not speculation.
Well, I tried to make sure that my speculations were qualified as such (and I consider speculation to be a valid component of discussion). If my speculations came across as assertions, I apologize. Kind regards, Stjepan

on Wed Aug 13 2008, "Stjepan Rajko" <stipe-AT-asu.edu> wrote:
Well, then I'm afraid that Boost.Parameter can never help you :-)
With Boost.Parameter it would look like: class named_constructable { public: // a macro could extend the number of parameters if desired template<typename FM0, typename FM1> named_constructable(const FM0 &a0, const FM1 &a1) { label = (a0,a1)[_label]; size = (a0,a1)[_size]; } std::string label; int size; }; But if you use the recommended method of building constructors, Boost.Parameter also supports: named_constructable x0("hello", 1); named_constructable x0("hello", _size=1); named_constructable x0(_label="hello", _size=1); named_constructable x0(_size=1, _label="hello"); and in this case (using deduced parameters), even named_constructable x0(1, "hello");
They didn't, but I did find your presumption that we hadn't already gone over this ground in designing the library rather... premature. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Wed, Aug 13, 2008 at 2:03 PM, David Abrahams <dave@boostpro.com> wrote:
:-) I should say, that was what I was trying to accomplish when I wrote the fusion-based utility this morning. As you've gone through my rationale and explained how it was mostly unfounded (and thank you for the clarifications), the "not using Boost.Parameter" part can go away.
If I had seen that in the docs first instead of the recommended approach I would have given Boost.Parameter a try. Instead, I happened to look at a page that told me I can't do something like that code snippet (the section 2.3 "must" fiasco).
This is really cool, but I'm fine with just named parameters, and prefer that over using the macro.
I don't know where I presumed that :-) It just turned out that the documentation (erroneously) indicated that I couldn't do something I wanted to do using Boost.Parameter, so I gave up and tried implementing something different. Stjepan

David Abrahams wrote:
I still stand by that. The map-lookup code in the library is really the simple part. Yes, using fusion for it *might* simplify the code, but it would also make our portability worse and likely increase the compile times. -- Daniel Wallin BoostPro Computing http://www.boostpro.com
participants (7)
-
Daniel Wallin
-
David Abrahams
-
Felipe Magno de Almeida
-
Paul A Bristow
-
Peter Dimov
-
Steven Watanabe
-
Stjepan Rajko