On Jan 10, 2008 7:58 PM, Joel de Guzman
Tobias Schwinger wrote:
Joel de Guzman wrote:
Single function:
I'm a strong advocate of smaller is better. Modularity matters. Big classes (or in this case function objects), metaprogram blobs (i.e. traits), humongous enums, and all such sort of dinosours :-) are best avoided. They are convenient up to a certain extent and becomes unmanageable beyond a certain limit.
In all of my use cases, I have N functions that are provided elsewhere and I have no control over (e.g. parser functions). I insist that this is the more common use case. Grouping them into a single big struct is an unnecessary and cumbersome step.
Still, if people insist, I outlined a way to convert the big function object to smaller 'bound' objects, in another post. Just bind 'em into smaller function chunks.
I think it's a misconception to assume that a single function object will be inherently big.
It's not about "big". It's about modular vs. monolithic. The best designs, IMO, are modular.
It's just another degree of freedom (namely what to look up) exposed to the user. Whether one can appreciate it seems a matter of taste to me...
A ---> B: function := L(I): functions[I]() B ---> A: transform(cases, L(I): make_pair<I>(bind(function,I())))
...and I happen to prefer the first transform.
I guess that reasoning is due to your thinking that the one monolithic automation functor is more common. That's where I disagree, vehemently(!). I've got lots of use cases that show otherwise. I assert that the more common case is to have as input N functions (member/free function pointers, binded functions, boost.functions, etc) and the switch_ is used to dispatch.
Let me emphasize this to the strongest extent: A single monolithic function for the cases in a switch facility is simply and utterly wrong, wrong and wrong! :-P If the design stands as it is, I better write my own. But it won't get my vote.
FWIW, I have a use case in which a single function is used for all the cases, but it doesn't seem monolithic. Structurally it is very similar to the example given in the proposed library's documentation - the case function looks something like this: // get_port_c behaves similarly to fusion::at_c // port_t<T> is a run-time polymorphic class derived from port // the goal of this whole thing is to return a port * (port is the base class of a run-time polymorphic class hierarchy) from an object of some type T (T is required to model a certain concept - Port) template<typename Component> struct get_port_case { typedef void result_type; template<class Case> void operator()(Case) const { ret.reset(new port_t< typename result_of::get_port_c< Component, Case::value, >::type >(get_port_cCase::value(c))); } get_port_case(Component& c, std::auto_ptr<port> &ret) : c(c), ret(ret) {} Component &c; std::auto_ptr<port> &ret; }; It is used like this (the number of ports is known at compile time but not at "coding time"): template<typename Component> std::auto_ptr<port> get_port(Component &c, int port_num) { std::auto_ptr<port> ret; typedef mpl::range_c< int, 0, mpl::size< typename traits_of<Component>::type::ports> ::value> range; boost::switch_<range>(port_num, detail::get_port_case<Component>(c, ret)); return ret; } "A" seems ideal for this use case. I have trouble seeing how to use "B" for it (easily) without making it so that the index is passed to the function object and allowing something like case<range>(...). But I might not be seeing all the possibilities. How can I implement this using "B"? Regardless of this example, if there isn't a "one size fits all" interface that can be implemented in a lightweight enough fashion to satisfy everyone, * should the Switch library provide both interfaces in separate functions (even if they are both implemented with their own PP)?; or * maybe the submitted library should have a different name/more clearly stated scope, as Tobias has suggested? Kudos to all of you for trying so hard at finding a solution for this! Stjepan