Dispatcher Library Initial Implementation

Hi Everyone, Uploaded to the vault [0] is the implementation of the dispatcher library. If there is enough interest, I would like to propose this library for inclusion into the Boost C++ Library. The documentation is yet unfinished, though I am not very sure about the contents of the documentation. However, I have put enough documentation with regards to how the library is designed, and used. A synopsis on how it can be used can be summed up in the following example code: [start code] #include <boost/dispatch.hpp> using boost::dispatch; //... dispatcher<int()> d; d[0] = int_function_1; d[1] = int_function_2; d[0]() // calls `int_function_1' d[1]() // calls `inf_function_2' d.unset(1); try { d[1]() // throws an unregistered_handler exception } catch (unregistered_handler & e) { // .. do nothing }; dispatcher<int()>::invoke_ d; d[1] = int_function_2; d << 0 << 1 ; // calls d[0] and d[1] in specified order. [end code] It also supports multiple argument callback registration (ala Signals): [start code] dispatcher<int (double, int, std::string)> d; d[0] = function_returns_int_and_takes_double_int_and_string; [end code] It also allows for parameterized index type definition: [start code] dispatcher<int(int), std::string> d; d["zero"] = function_returns_int_and_takes_int; [end code] The included documentation (doc/html) contains more information on the uses of the library, while the unit test (test/dispatch_test.cpp) can be referred to regarding the actual details on the usage. The project is currently hosted in sourceforge [1] and has been tested/built against RC_1_34_0 using MSVC 8.0 -- this is configured to be built only with BBv2. Have a great day everyone! Your comments and insights will be most appreciated. References: [0] Dispatcher Initial Implementation http://tinyurl.com/q9oux [1] Sourceforge.net project page http://tinyurl.com/mo2ds -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/13/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
Hi Everyone,
Uploaded to the vault [0] is the implementation of the dispatcher library. If there is enough interest, I would like to propose this library for inclusion into the Boost C++ Library. The documentation is yet unfinished, though I am not very sure about the contents of the documentation. However, I have put enough documentation with regards to how the library is designed, and used.
[snip code]
I'm sorry, but I don't see how this is a big advantage over using a std::map<int, boost::function<T> >. Kevin Spinar

On 7/14/06, Kevin Spinar <spinarkm@gmail.com> wrote:
On 7/13/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[snip code]
I'm sorry, but I don't see how this is a big advantage over using a std::map<int, boost::function<T> >.
Here's a case which is a significant advantage over std::map<int, boost::function<T> > : [start code] struct my_validator { bool operator () (const std::string & str) { return str.size() >= 5; } }; struct my_router { std::string operator() (const std::string & str) { std::string temp = ""; // perform hashing/transformation here return temp; } }; // ... dispatcher <void (std::string), std::string, my_validator, my_router> d; d["1234"] = void_function_takes_string; // will throw an invalid_index<std::string> exception d["12345"] = void_function_takes_string; // will map the callback to the hash // of "12345" as defined in the router policy d["23456"]("my string input"); // if "23456" hashes the same as "12345" // then `void_function_takes_string' is called where "my string input" // is passed accordingly [end code] This is called strategized index validation, and strategized routing implementation -- both being concepts that will need much documentation. This is documented in the dispatcher.design.dispatcher section in the included documentation. -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

Dean Michael Berris ha scritto:
On 7/14/06, Kevin Spinar <spinarkm@gmail.com> wrote:
On 7/13/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[snip code] I'm sorry, but I don't see how this is a big advantage over using a std::map<int, boost::function<T> >.
Here's a case which is a significant advantage over std::map<int, boost::function<T> > :
[start code]
struct my_validator { bool operator () (const std::string & str) { return str.size() >= 5; } };
struct my_router { std::string operator() (const std::string & str) { std::string temp = ""; // perform hashing/transformation here return temp; } };
// ... dispatcher <void (std::string), std::string, my_validator, my_router> d; d["1234"] = void_function_takes_string; // will throw an invalid_index<std::string> exception d["12345"] = void_function_takes_string; // will map the callback to the hash // of "12345" as defined in the router policy d["23456"]("my string input"); // if "23456" hashes the same as "12345" // then `void_function_takes_string' is called where "my string input" // is passed accordingly
[end code]
This is called strategized index validation, and strategized routing implementation -- both being concepts that will need much documentation. This is documented in the dispatcher.design.dispatcher section in the included documentation.
Hmmm... I fail to see two things: 1) why would I want/need such features on a dispatcher? 2) what it is the relationship between such features and the dispatcher? I mean, index validation and routing are (to me at least) two concepts completely unrelated to dispatching, so IMHO it would make much more sense to have a generic container that could store any value type, not only functions. Once you have such container, you can make a decent dispatcher by just putting boost::functions in it. Ganesh

On 7/15/06, Alberto Ganesh Barbati <abarbati@iaanus.com> wrote:
1) why would I want/need such features on a dispatcher?
This is a good question, and the answer would be "it depends on the use case". For index validation, an example would be if you were taking runtime input and using them as indexes, and you want to enforce that indexes be of a certain kind (or setting rules on what valid indexes should be). This would come in handy in case you were registering dynamically loaded functions to runtime values as indexes and want to make sure that the indexes conform to some convention (in cases where you're implementing protocol handlers, and the schema is dynamic like XML). For index manipulation/hashing/transformation/routing the case would be for "normalizing" the valid indexes (or even the invalid indexes). That means trivial stuff like rounding up numbers, generating an md5 hash, doing case normalization, and the likes can be decoupled from the actual dispatching. Of course, both of these are optional, and there are default "dumb" routing and validation strategies that are by default implemented by the dispatcher
2) what it is the relationship between such features and the dispatcher? I mean, index validation and routing are (to me at least) two concepts completely unrelated to dispatching, so IMHO it would make much more sense to have a generic container that could store any value type, not only functions. Once you have such container, you can make a decent dispatcher by just putting boost::functions in it.
Think of it as "message routing" if "dispatching" conjures up a lot of different things for you. For me at least, this is how I understand how a dispatcher works: Someone (client code) refers to a person (dispatcher) and asks "can you please get me <index>?" -- the person (dispatcher) is trained to see if <index> is a valid index (validation) and normalizes/transforms the index (routing) to pull up the correct object/function requested. As far as I understand it, if you don't need the validation and routing, then you don't need the dispatcher -- but in cases that you do need that, or a runtime switch replacement, then it should come in handy. The goal of the dispatcher is to facilitate the registration and dispatching (or calling) of runtime functions, and providing a dynamically reprogrammable interface for such a facility. For certain problem domains, this appeals as a simple and extensible solution -- in other cases, it might be overkill. HTH -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

Dean Michael Berris ha scritto:
Of course, both of these are optional, and there are default "dumb" routing and validation strategies that are by default implemented by the dispatcher
2) what it is the relationship between such features and the dispatcher? I mean, index validation and routing are (to me at least) two concepts completely unrelated to dispatching, so IMHO it would make much more sense to have a generic container that could store any value type, not only functions. Once you have such container, you can make a decent dispatcher by just putting boost::functions in it.
Think of it as "message routing" if "dispatching" conjures up a lot of different things for you. For me at least, this is how I understand how a dispatcher works:
Someone (client code) refers to a person (dispatcher) and asks "can you please get me <index>?" -- the person (dispatcher) is trained to see if <index> is a valid index (validation) and normalizes/transforms the index (routing) to pull up the correct object/function requested.
As far as I understand it, if you don't need the validation and routing, then you don't need the dispatcher -- but in cases that you do need that, or a runtime switch replacement, then it should come in handy.
The goal of the dispatcher is to facilitate the registration and dispatching (or calling) of runtime functions, and providing a dynamically reprogrammable interface for such a facility. For certain problem domains, this appeals as a simple and extensible solution -- in other cases, it might be overkill.
Thanks for the explanation. Your two hints about validation/routing being optional makes me think that you think I am objecting those concepts. Contrariwise, I do think the concepts of validation/routing are interesting *per se*. Those concepts can of course be applied to dispatching (or retrieving/calling runtime functions, or whatever you prefer to call that) but have a much more general value, IMHO. For example, here's my use case: I am writing a videogame and I need to load a lot of texture images from disk files. Of course I don't want to load the same image twice. A simple map where the key is the file pathname is not a good tool, because there may be different pathnames pointing to the same file. Let's assume that using some kind of canonical form of pathnames is sufficient to uniquely identify the file. So my "routing" step is a conversion from a pathname to its canonical form. My "validation" step is a check that the file actually exists and identifies a supported image format. See? A container like the one you described could be very useful to me, yet I am going to store objects which are not functions nor related in any ways to functions. Does it make sense? Ganesh

On 7/15/06, Alberto Ganesh Barbati <abarbati@iaanus.com> wrote:
Thanks for the explanation.
My pleasure. Thanks for taking the time to ask the very important questions. :)
Your two hints about validation/routing being optional makes me think that you think I am objecting those concepts. Contrariwise, I do think the concepts of validation/routing are interesting *per se*. Those concepts can of course be applied to dispatching (or retrieving/calling runtime functions, or whatever you prefer to call that) but have a much more general value, IMHO.
I agree with you on this -- it seems like they themselves are very handy concepts and implementing a common and reusable/extensible interface for validation and routing along with a more generic associative container are very interesting to implement indeed.
For example, here's my use case: I am writing a videogame and I need to load a lot of texture images from disk files. Of course I don't want to load the same image twice. A simple map where the key is the file pathname is not a good tool, because there may be different pathnames pointing to the same file. Let's assume that using some kind of canonical form of pathnames is sufficient to uniquely identify the file. So my "routing" step is a conversion from a pathname to its canonical form. My "validation" step is a check that the file actually exists and identifies a supported image format. See? A container like the one you described could be very useful to me, yet I am going to store objects which are not functions nor related in any ways to functions.
Does it make sense?
Yes, this makes a lot of sense. It seems like the dispatcher concept in itself can exist separately or along with the validator+router+associative_container concepts. Definitely it makes a lot of sense to have an implementation such as what you are suggesting, and it looks like I already have some code work with to come up with something generally useful -- aside from the dispatcher. Thanks very much for the ideas! :) -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

"Dean Michael Berris" <mikhailberis@gmail.com> writes:
// ... dispatcher <void (std::string), std::string, my_validator, my_router> d; d["1234"] = void_function_takes_string; // will throw an invalid_index<std::string> exception d["12345"] = void_function_takes_string; // will map the callback to the hash // of "12345" as defined in the router policy d["23456"]("my string input"); // if "23456" hashes the same as "12345" // then `void_function_takes_string' is called where "my string input" // is passed accordingly
[end code]
This is called strategized index validation, and strategized routing implementation -- both being concepts that will need much documentation. This is documented in the dispatcher.design.dispatcher section in the included documentation.
There's too much coupling of unrelated concepts in this design, it seems to me. All that's required to do this is a generic wrapper over string: template <class T, class Validate, class Hash> struct strategized_index { // Throws an exception unless Validate()(x). Stores x as a member strategized_index(T const& x); // returns Hash()(*this.x) < Hash()(rhs) bool operator<(strategized_index const& rhs); }; If you really need comparison and hashing to be stateful, then you do need to parameterize the container... but you still don't need to tie it to dispatching; just choose to store boost::function objects as Kevin suggested: template <class K, class V, class Validate, class Hash> struct strategized_unordered_map; no? -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 7/15/06, David Abrahams <dave@boost-consulting.com> wrote:
"Dean Michael Berris" <mikhailberis@gmail.com> writes:
This is called strategized index validation, and strategized routing implementation -- both being concepts that will need much documentation. This is documented in the dispatcher.design.dispatcher section in the included documentation.
There's too much coupling of unrelated concepts in this design, it seems to me. All that's required to do this is a generic wrapper over string:
Now that you've mentioned it, I have to agree with you with that point. It seems that the routing and validation are useful concepts in themselves especially in many different contexts involving runtime input and input manipulation -- especially useful when dealing with generic associative containers.
template <class T, class Validate, class Hash> struct strategized_index { // Throws an exception unless Validate()(x). Stores x as a member strategized_index(T const& x);
// returns Hash()(*this.x) < Hash()(rhs) bool operator<(strategized_index const& rhs); };
If you really need comparison and hashing to be stateful, then you do need to parameterize the container... but you still don't need to tie it to dispatching; just choose to store boost::function objects as Kevin suggested:
template <class K, class V, class Validate, class Hash> struct strategized_unordered_map;
no?
Yes, that makes a lot of sense. Perhaps separating the index and router concepts as parameters to a generic container (much like a map, which maps objects to keys) which the dispatcher itself just uses, then they can be useful components in themselves. Thank you very much for the insights. :) I would most probably take this path and explore it further. Have a great weekend everyone! -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com
participants (4)
-
Alberto Ganesh Barbati
-
David Abrahams
-
Dean Michael Berris
-
Kevin Spinar