which tag dispatch convention to use

Hello, As I'm preparing the dataflow library for submission (a.k.a. signal network gsoc project), I'm revisiting some of the design choices. Since I'm not too experienced with generic library design, I was wondering if anyone would share some advice on which tag dispatch convention to use. For my specializable functions, I adapted what is used in fusion, e.g.: template<typename ProducerTag, ConsumerTag> struct operation_impl; template<> struct operation_impl<some_producer_tag, some_consumer_tag> { template<typename P, typename C> struct apply { typedef some_result_type type; static type call(P &p, C &c) { ... } }; }; and then free function `operation` extracts the tags from its arguments, calls operation_impl, and returns the result. Could someone tell me what the advantages/disadvantages of this approach are compared to, say, template<> struct operation_impl<some_producer_tag, some_consumer_tag> { // specify result type using result_type typedef or template<> struct result // ... template<typename P, typename C> some_result_type operator()(P &p, C &c) { ... }; }; , or the technique described in: http://www.boost.org/more/generic_programming.html, or any other tag dispatching convention I should be aware of? Thanks in advance! Stjepan

Ping? Anyone? I'd be very grateful for any advice... On 10/15/07, Stjepan Rajko <stipe@asu.edu> wrote:
Hello,
As I'm preparing the dataflow library for submission (a.k.a. signal network gsoc project), I'm revisiting some of the design choices. Since I'm not too experienced with generic library design, I was wondering if anyone would share some advice on which tag dispatch convention to use.
For my specializable functions, I adapted what is used in fusion, e.g.:
template<typename ProducerTag, ConsumerTag> struct operation_impl;
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { template<typename P, typename C> struct apply { typedef some_result_type type;
static type call(P &p, C &c) { ... } }; };
and then free function `operation` extracts the tags from its arguments, calls operation_impl, and returns the result.
Could someone tell me what the advantages/disadvantages of this approach are compared to, say,
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { // specify result type using result_type typedef or template<> struct result // ...
template<typename P, typename C> some_result_type operator()(P &p, C &c) { ... }; };
, or the technique described in: http://www.boost.org/more/generic_programming.html,
or any other tag dispatching convention I should be aware of?
Thanks in advance!
Stjepan

Stjepan Rajko wrote:
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { // specify result type using result_type typedef or template<> struct result // ...
template<typename P, typename C> some_result_type operator()(P &p, C &c) { ... }; };
FWIW, this can have data members, which `operator()` can access. Regards, -- Shunsuke Sogame

On 10/19/07, shunsuke <pstade.mb@gmail.com> wrote:
Stjepan Rajko wrote:
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { // specify result type using result_type typedef or template<> struct result // ...
template<typename P, typename C> some_result_type operator()(P &p, C &c) { ... }; };
FWIW, this can have data members, which `operator()` can access.
Indeed - and now that you point it out, I think this brings up a lot of very interesting possibilities for the library which I haven't thought of before. Additionally, I'm liking that this approach integrates with Boost.ResultOf. Perhaps it will be worth switching to it. Thanks for your thoughts! Stjepan

Stjepan Rajko wrote:
Indeed - and now that you point it out, I think this brings up a lot of very interesting possibilities for the library which I haven't thought of before. Additionally, I'm liking that this approach integrates with Boost.ResultOf. Perhaps it will be worth switching to it.
As I recall, <boost/numeric/functional.hpp> @ Boost.Accumulators shows an interesting way. That's really interesting. Regards, -- Shunsuke Sogame

Stjepan Rajko wrote:
For my specializable functions, I adapted what is used in fusion, e.g.:
template<typename ProducerTag, ConsumerTag> struct operation_impl;
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { template<typename P, typename C> struct apply { typedef some_result_type type;
static type call(P &p, C &c) { ... } }; };
IIRC, using this form makes operation_impl<some_producer_tag, some_consumer_tag> into an MPL metafunction class, which makes it usable with all of the various metafunction algorithms. That's important if you need to use MPL algorithms across sequences of producer/consumer types.
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { // specify result type using result_type typedef or template<> struct result // ...
template<typename P, typename C> some_result_type operator()(P &p, C &c) { ... }; };
The advantage of this one relative to the first one is that, here, operation_impl<some_producer_tag, some_consumer_tag> is a function object while the first one requires you to dig out the ::apply<P, C> member and use the static "call". However, this version doesn't provide direct interoperability with MPL.
, or the technique described in: http://www.boost.org/more/generic_programming.html,
or any other tag dispatching convention I should be aware of?
I still prefer the technique described at the web page you mention, because it's more robust with the evolution of the concepts in the library. For example, note in the advance() example there that we only provide advance_dispatch overloads for input_iterator_tag, bidirectional_iterator_tag, and random_access_iterator_tag. However, the actual tag "hierarchy" looks like this: struct input_iterator_tag { }; struct output_iterator_tag { }; struct forward_iterator_tag : virtual input_iterator_tag, virtual output_iterator_tag { }; struct bidirectional_iterator_tag : virtual forward_iterator_tag { }; struct random_access_iterator_tag : virtual bidirectional_iterator_tag { }; Notice that an iterator with that forward_iterator_tag will still work with the tag-dispatching approach from the web page (because of the implicit conversion from forward_iterator_tag to input_iterator_tag). To make this work with the specialization-based approach used in the top two examples you gave, you would have to add an extra specialization for forward_iterator_tag. That's only a little extra work, but it would have to be done any time someone introduces a new concept, e.g., struct contiguous_iterator_tag : virtual random_access_iterator_tag { }; - Doug

Douglas Gregor wrote:
Stjepan Rajko wrote:
For my specializable functions, I adapted what is used in fusion, e.g.:
template<typename ProducerTag, ConsumerTag> struct operation_impl;
template<> struct operation_impl<some_producer_tag, some_consumer_tag> { template<typename P, typename C> struct apply { typedef some_result_type type;
static type call(P &p, C &c) { ... } }; };
IIRC, using this form makes operation_impl<some_producer_tag, some_consumer_tag> into an MPL metafunction class, which makes it usable with all of the various metafunction algorithms. That's important if you need to use MPL algorithms across sequences of producer/consumer types.
Yes, that way, fusion can take advantage of MPL facilities like mpl lambda: very useful, IMO. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On 10/23/07, Douglas Gregor <dgregor@osl.iu.edu> wrote:
Stjepan Rajko wrote:
For my specializable functions, I adapted what is used in fusion, e.g.:
[snip]
IIRC, using this form makes operation_impl<some_producer_tag, some_consumer_tag> into an MPL metafunction class, which makes it usable with all of the various metafunction algorithms. That's important if you need to use MPL algorithms across sequences of producer/consumer types.
Ahh, I see. I had no idea where this convention came from :-) I'll have to think about how useful direct MPL interoperability is in the dataflow lib...
[snip]
, or the technique described in: http://www.boost.org/more/generic_programming.html,
or any other tag dispatching convention I should be aware of?
I still prefer the technique described at the web page you mention, because it's more robust with the evolution of the concepts in the library. For example, note in the advance() example there that we only [snip]
OK, I see... But then I suppose the tricky part becomes getting the exact result type, if it's not fixed... Doug, thank you very much for such a detailed response! Now I have some thinking to do :-) Stjepan
participants (4)
-
Douglas Gregor
-
Joel de Guzman
-
shunsuke
-
Stjepan Rajko