Any interest in dynamic dispatching mechanism from "interface" to template functions ?

Hello all, The idea behind is not new and consists in dispatching a call of an interface function to the correct template instance, depending on the runtime or compile time parameters. The field of application is (to me) the release of static/dynamic libraries including functions with a predefined set of types combination. This way, the library (compiled once) can easily be plugged to another framework or a script program (I personally use it with boost.python). An example of use could be (general idea, not a real code of course): // -------------- template <class real_type> int template_function(real_type const& r_class1, double param_d, int& param_out) { // ... return 0; } int interface_function(MyInterface const* i_class1, variant const& param, variant& out) { // return value int return_value; // constructs the dispatcher object with a reference to the values passed to interface_function dispatcher<int, MyInterface const*, variant const&, variant&> dispatch_object(return_value, i_class1, param, out); bool res = dispatch_object.calls_first( fusion::vector_tie( template_function < class_t1 >, template_function < class_t2 > //... ) ); if(res) return return_value; return -1; } // -------------- The dispatch_object tests for the convertibility of each instance ui of the arguments Ui of the interface function, with the corresponding argument Ti of the template function. The type of matching is defined by external objects, with a simple interface providing two methods: a convertibility test and a conversion. The types are "cleaned" in order to have a limited number of conversion structures. Currently this mechanism is used in a generic image processing library. The images have all the same virtual interface and the dispatch calls the correct template function based on the concrete type of the image. It also includes a "back conversion" for Ui's that are non-const reference or pointers. The dispatch mainly makes use of boost.fusion, boost.mpl and boost.function_types. I have checked a little bit the Boost.Vault but, correct me if I am wrong, the current dispatching mechanisms do not address exactly this issue. The code is already open-source released under the boost license. I can put some samples in the Vault if there is any interest. Your comments are very welcome ! Kind regards, Raffi Enficiaud

On Jul 1, 2010, at 6:32 AM, Raffi Enficiaud wrote:
The idea behind is not new and consists in dispatching a call of an interface function to the correct template instance, depending on the runtime or compile time parameters. The field of application is (to me) the release of static/dynamic libraries including functions with a predefined set of types combination. This way, the library (compiled once) can easily be plugged to another framework or a script program (I personally use it with boost.python).
Does this make sense as a Boost.Python extension? -- David Abrahams BoostPro Computing http://boostpro.com

David Abrahams <dave <at> boostpro.com> writes:
Does this make sense as a Boost.Python extension?
The idea is to make things in the opposite way :) A similar mechanism is already implemented in Boost.Python (the "implicit conversion" and the registration of manual convertors + matching of arguments with the function signatures). The mechanism in Boost.Python is relying on a global registry constructed when the python extension are loaded. In what I proposed, this "registry" is given by the set of convertors available at compilation time. These convertors are in a sense similar to the "manual convertors" in boost.python. PS.: I refer to manual convertors as the structures declaring a special kind of conversion from python to C++ (with the two stages conversion static void* convertible(PyObject* obj_ptr) + static XXX* do_convert(PyObject* obj_ptr, void * pos))

On 01/07/10 11:32, Raffi Enficiaud wrote:
An example of use could be (general idea, not a real code of course): // --------------
template <class real_type> int template_function(real_type const& r_class1, double param_d, int& param_out) { // ... return 0; }
int interface_function(MyInterface const* i_class1, variant const& param, variant& out) { // return value int return_value;
// constructs the dispatcher object with a reference to the values passed to interface_function dispatcher<int, MyInterface const*, variant const&, variant&> dispatch_object(return_value, i_class1, param, out);
bool res = dispatch_object.calls_first( fusion::vector_tie( template_function < class_t1 >, template_function < class_t2 > //... ) ); if(res) return return_value; return -1; }
Isn't that what apply_visitor already does, albeit it only deals with variant and not base classes?

Mathias Gaunard <mathias.gaunard <at> ens-lyon.org> writes:
Isn't that what apply_visitor already does, albeit it only deals with variant and not base classes?
Not exactly (as far as I understood apply_visitor). The dispatch is a mapping from a call to an interface function (variant, virtual abstract classes, etc) to the appropriate template function doing the job. In the example I provided, the "MyInterface const*" class will be tested for exact matching with the template "real_type const&", while the variants will be tested for convertibility and "back" convertibility at the end of the call (tell me if it is not clear :) ). Plus the call succeeds when all the parameters have been matched. So the first call ("template_function < class_t1 >") succeeds if - "i_class1" (MyInterface const *) can be dynamic_casted to "class_t1 const&", - "param" (variant const&) can be casted to double and - "param_out" (int) can be casted to variant& ("out"). Otherwise the second template function ("template_function < class_t2 >") is tested. The types which are tested for matching are either convertibles and/or exact, depending on the convertor. Currently I have 3 different type of convertors: - compile time: which map for instance an uint8 to a float - dynamic_cast: from a base class to a concrete class - runtime: depending on the content of the object (variant)

On 07/01/10 05:32, Raffi Enficiaud wrote:
Hello all,
The idea behind is not new and consists in dispatching a call of an interface function to the correct template instance, depending on the runtime or compile time parameters. The field of application is (to me) the release of static/dynamic libraries including functions with a predefined set of types combination. This way, the library (compiled once) can easily be plugged to another framework or a script program (I personally use it with boost.python).
An example of use could be (general idea, not a real code of course): // --------------
template <class real_type> int template_function(real_type const& r_class1, double param_d, int& param_out) { // ... return 0; }
int interface_function(MyInterface const* i_class1, variant const& param, variant& out) { // return value int return_value;
// constructs the dispatcher object with a reference to the values passed to interface_function dispatcher<int, MyInterface const*, variant const&, variant&> dispatch_object(return_value, i_class1, param, out);
bool res = dispatch_object.calls_first( fusion::vector_tie( template_function < class_t1 >, template_function < class_t2 > //... ) ); if(res) return return_value; return -1; }
// --------------
The dispatch_object tests for the convertibility of each instance ui of the arguments Ui of the interface function, with the corresponding argument Ti of the template function. The type of matching is defined by external objects, with a simple interface providing two methods: a convertibility test and a conversion. The types are "cleaned" in order to have a limited number of conversion structures. [snip] It also includes a "back conversion" for Ui's that are non-const reference or pointers. [snip] I have checked a little bit the Boost.Vault but, correct me if I am wrong, the current dispatching mechanisms do not address exactly this issue. [snip] The test driver code:
http://preview.tinyurl.com/2a9dlun tests something similar; however, it doesn't use fusion::vector to store the argument references. Instead it uses: template<typename... ArgTypes> struct void_ptr_array { void* my_ptrs[sizeof...(ArgTypes>; ... }; The void* are cast to whatever the ArgTypes indicate the actual variables types happen to be. However, I'm not sure that's completely portable. OTOH, one advantage, at least I think, is that the abstract args and their corresponding concrete values can be stored (or at least pointers to them) in the same memory locations by just changing the ArgTypes... . FWIW, I did try using a fusion container; however, I was using one container for the abstract args, and another for the concrete args, and pop_front'ing from the abstract arg container while push_backing to the concrete arg container, which I thought was a bit of a waste. Anyway, the dispatching code in reify_apply.hpp: http://preview.tinyurl.com/2ah2kpx can be used for either a variant type structure (like that in: variadic_templates/boost/composite_storage/pack/container_one_of_maybe.hpp ) by using reifier_switch.hpp, or one with virtual functions to do the dispatching, like in the visitor pattern, as done with reifier_visitor.hpp. The test driver uses either one based on the value of the REIFIER_VISITOR macro. It differs from the apply_visitor in that it can be used for any number of arguments, since sizeof...(ArgTypes) can be any number. What maybe different from your method, I think, is suggested by:
The dispatch_object tests for the convertibility of each instance ui of the arguments Ui of the interface function, with the corresponding argument Ti of the template function.
I think that job is done by the: apply_ftor_check_args in apply_unpack.hpp. I think mabye what you mean by:
It also includes a "back conversion" for Ui's that are non-const reference or pointers.
is done by the: arg_type*ap=static_cast<arg_type*>(vp); return *ap; in void_ptr_array::project in replace_source_with_target_ptr.hpp. -regards, Larry

Larry Evans <cppljevans <at> suddenlink.net> writes:
The test driver code:
http://preview.tinyurl.com/2a9dlun
tests something similar; however, it doesn't use fusion::vector to store the argument references. Instead it uses:
template<typename... ArgTypes> struct void_ptr_array { void* my_ptrs[sizeof...(ArgTypes>; ... };
The void* are cast to whatever the ArgTypes indicate the actual variables types happen to be.
Ok, I think I got the idea. In the current implementation, I wanted the types qualified and avoiding "void*". So I can also perform compile-time checks on the consistency of the convertors available.
However, I'm not sure that's completely portable. OTOH, one advantage, at least I think, is that the abstract args and their corresponding concrete values can be stored (or at least pointers to them) in the same memory locations by just changing the ArgTypes... .
If possible, reference to initial objects are stored. If not, a holder creates an appropriate temporary type during the call. This temporary is then reconverted to the interface type at the end of the call, if the interface type is a non-const ref, or the convertor tells that the matching needs a back-conversion. So similar types (trivial conversion) for instance will not be copied or dereferenced at all. Same for the conversion from a base class to a daughter class.
FWIW, I did try using a fusion container; however, I was using one container for the abstract args, and another for the concrete args, and pop_front'ing from the abstract arg container while push_backing to the concrete arg container, which I thought was a bit of a waste.
In fact, there is a bit of indirection in my implementation. The fusion container I use stores the interface parameters. The other container is an mpl sequence since there is no value associated to it before the call to the function. In fact, I create a fusion sequence of "convertor_wrapper", each having an implicit conversion to the template argument type and a special destructor. The implicit conversion calls the underlying convertor. These conversions are made if and only if all the convertor_wrapper in the chain of conversion are able to return a value corresponding to the template argument, otherwise no conversion is performed. I use for that "fusion::invoke", with the sequence of convertor_wrappers. I think this is quite ok for the portability, since it only involves the implicit cast operators of each convertor_wrapper (I tested it on MSVC 2008Expr, GCC 4.01 & GCC 4.4.1).
Anyway, the dispatching code in reify_apply.hpp:
http://preview.tinyurl.com/2ah2kpx
can be used for either a variant type structure (like that in:
variadic_templates/boost/composite_storage/pack/container_one_of_maybe.hpp ) by using reifier_switch.hpp,
or one with virtual functions to do the dispatching, like in the visitor pattern, as done with reifier_visitor.hpp.
I will have a look at this code soon !
The test driver uses either one based on the value of the REIFIER_VISITOR macro.
It differs from the apply_visitor in that it can be used for any number of arguments, since sizeof...(ArgTypes) can be any number.
What maybe different from your method, I think, is suggested by:
The dispatch_object tests for the convertibility of each instance ui of the arguments Ui of the interface function, with the corresponding argument Ti of the template function.
I think that job is done by the:
apply_ftor_check_args
in apply_unpack.hpp.
Almost, but not exactly. For instance line 83 of apply_unpack.hpp: typename function_types:: can_be_called<Functor(Args const&...)>::type is_ftor_args_callable; The fact is that the Functor itself is not always directly callable with the Args, and there is two sequences of Args that should be considered: the one for the interface and the other for the arguments of the template function. So the level of indirection induced by the convertors mentioned earlier should be considered. For instance a base class pointer to a derived class reference (again... :) ). All these logics are embedded in the convertors, defined for each pair of types (interface, concrete), which make it very easy to extend to new types.
I think mabye what you mean by:
It also includes a "back conversion" for Ui's that are non-const reference or pointers.
is done by the:
arg_type*ap=static_cast<arg_type*>(vp); return *ap;
in void_ptr_array::project in replace_source_with_target_ptr.hpp.
-regards, Larry
So here again, I think there is a need for an indirection level. The conversion is not always static (std::map& to variant& for instance). But maybe I missed some point ? Thanks for your feedback ! Regards, Raffi

On 07/01/10 17:48, Raffi Enficiaud wrote:
Larry Evans <cppljevans <at> suddenlink.net> writes:
The test driver code:
http://preview.tinyurl.com/2a9dlun
tests something similar; [snip] What maybe different from your method, I think, is suggested by:
The dispatch_object tests for the convertibility of each instance ui of the arguments Ui of the interface function, with the corresponding argument Ti of the template function.
I think that job is done by the:
apply_ftor_check_args
in apply_unpack.hpp.
Almost, but not exactly. For instance line 83 of apply_unpack.hpp:
typename function_types:: can_be_called<Functor(Args const&...)>::type is_ftor_args_callable;
The fact is that the Functor itself is not always directly callable with the Args, and there is two sequences of Args that should be considered: the one for the interface and the other for the arguments of the template function. So the level of indirection induced by the convertors mentioned earlier should be considered. For instance a base class pointer to a derived class reference (again... :) ). All these logics are embedded in the convertors, defined for each pair of types (interface, concrete), which make it very easy to extend to new types.
Ah! This is was I was missing. IOW, dispatch_object must know the signature ( argument types) of 2 functions, the interface function and the template function and attempt conversions between corresponding argument types. In contrast, reify_apply simply converts each "abstract" argument to it's actual "concrete" value before 1st checking that the concrete values are consistent (using apply_ftor_check_args) with the Functor. IOW, there's just one function (or Functor) in reify_apply's case, and that's not examined until all conversions are done because only a runtime value of the abstract objects(the tag value in case of the reifier_switch and the virtual function table in the case of reifier_visitor) are used to convert to the concrete objects; whereas, with dispatch_object, 2 functions are used and the signatures of both are used to guide the conversion. [snip]
I think mabye what you mean by:
It also includes a "back conversion" for Ui's that are non-const reference or pointers.
is done by the:
arg_type*ap=static_cast<arg_type*>(vp); return *ap;
in void_ptr_array::project in replace_source_with_target_ptr.hpp.
-regards, Larry
So here again, I think there is a need for an indirection level. The conversion is not always static (std::map& to variant& for instance). But maybe I missed some point ?
I think maybe the problem is, as mentioned above, reify_apply solves a somewhat different problem than dispatch_object.
Thanks for your feedback !
You're welcome! -regards, Larry

On 07/01/10 20:30, Larry Evans wrote:
On 07/01/10 17:48, Raffi Enficiaud wrote: [snip]
The fact is that the Functor itself is not always directly callable with the Args, and there is two sequences of Args that should be considered: the one for the interface and the other for the arguments of the template function. So the level of indirection induced by the convertors mentioned earlier should be considered. For instance a base class pointer to a derived class reference (again... :) ). All these logics are embedded in the convertors, defined for each pair of types (interface, concrete), which make it very easy to extend to new types.
Ah! This is was I was missing. IOW, dispatch_object must know the signature ( argument types) of 2 functions, the interface function and the template function and attempt conversions between corresponding argument types. In contrast, reify_apply simply converts each "abstract" argument to it's actual "concrete" value before 1st checking that the concrete values are consistent (using apply_ftor_check_args) with the Functor. IOW, there's just one function (or Functor) in reify_apply's case, and that's not examined until all conversions are done because only a runtime value of the abstract objects(the tag value in case of the reifier_switch and the virtual function table in the case of reifier_visitor) are used to convert to the concrete objects; whereas, with dispatch_object, 2 functions are used and the signatures of both are used to guide the conversion.
[snip] This sounds something like functor in category theory: http://en.wikipedia.org/wiki/Functor What you have is 2 functions: f_i the interface function f_t the template function and you want someway to convert a call to the f_i to f_t. So, given: t_i0 f_i(t_i1, t_i2, ..., t_in) and: t_t0 f_t(t_t1, t_t2, ..., t_tn) you want a set of conversion functions: t_tj c_j(t_ij) so that you can call: t_t0 f_t(c_1(t_i1), c_2(t_i2), ... c_n(t_in) ) or something like that, right? -regards, Larry

Larry Evans <cppljevans <at> suddenlink.net> writes:
On 07/01/10 20:30, Larry Evans wrote: [snip] This sounds something like functor in category theory:
http://en.wikipedia.org/wiki/Functor
What you have is 2 functions:
f_i the interface function f_t the template function
and you want someway to convert a call to the f_i to f_t. So, given:
t_i0 f_i(t_i1, t_i2, ..., t_in)
and:
t_t0 f_t(t_t1, t_t2, ..., t_tn)
you want a set of conversion functions:
t_tj c_j(t_ij)
so that you can call:
t_t0 f_t(c_1(t_i1), c_2(t_i2), ... c_n(t_in) )
or something like that, right?
-regards, Larry
This is the general idea, but it does not really coincide with the notion of mathematical functor. These are homomorphism, which means that they preserve a mathematical structure. Well here, there is no such kind of structure I guess. The "objects" are here the types and their specific instance. Plus functors are arrows with one direction, while the c_i's are bidirectional (possible update of the interface parameters during their destruction). The convertors preserves composition, as functors do. So I would think about "mapping objects" rather than functors. But the call t_t0 f_t(c_1(t_i1), c_2(t_i2), ... c_n(t_in) ) is exactly the idea ! Kind regards, Raffi

On 07/02/10 07:20, Raffi Enficiaud wrote:
Larry Evans <cppljevans <at> suddenlink.net> writes: [snip]
This sounds something like functor in category theory:
[snip] This is the general idea, but it does not really coincide with the notion of mathematical functor. These are homomorphism, which means that they preserve a mathematical structure. Well here, there is no such kind of structure I guess. The "objects" are here the types and their specific instance. Plus functors are arrows with one direction, while the c_i's are bidirectional (possible update of the interface parameters during their destruction).
Yes, functors are one direction; however, there's also functor inverses, which go in the opposite direction: http://en.wikipedia.org/wiki/Natural_transformation also, page 6 (in Section 5) of: http://www.math.wisc.edu/~virk/notes/pre08/pdf/categories.pdf I found these by googling "isomorphic functors". -regards, Larry

Dear Larry, Larry Evans <cppljevans <at> suddenlink.net> writes:
On 07/02/10 07:20, Raffi Enficiaud wrote:
Larry Evans <cppljevans <at> suddenlink.net> writes: [snip]
This sounds something like functor in category theory:
[snip]
Ok now I understand what you meant. Indeed the purpose of the dispatcher is to find the appropriate functor depending on the input types (if we discard the return type). Suppose I have an "interface" function (mapping) f: X -> Y. X and Y \in C \times D are the elements on the interface level. Several implementations {t1, ...tn} exist at the template level for doing the job of f, then the dispatcher "d" should find the appropriate functor, in order to call the appropriate implementation. d: f \times X \times Y -> F st. F \in {t1, ... tn} \cup \emptyset F(f) : F(X) -> F(Y) So the dispatcher returns a functor F (or fails is no implementation is available), and do the call. In my case, I have for instance a image interface which is polymorphic on the pixel type and dimension of the spanned space. "f" at the interface level would be a "copy" from one image into another, the {t1... tn} are the real implementations (with for instance memcpy when appropriate). What deviates from this viewpoint is that the spaces of X and Y are not necessarily "categories" (no identity element, no special operations, etc.). Unless we define the indentity element as being the "function does nothing", X and Y are become functions and set of functions respectiveley ("copy at interface level" and the implementations of "copy at template level"). f is then mapping between the interface function and the set of template functions. F becomes the mapping (call) through the dispatcher: F(f): F(X) -> F(Y) F(X): function at the interface level F(Y): selected implementation (or void) However, in this view F(Y) does not depend on X. In my point of view of the dispatcher, the selection is made rather on the right side of the arrow, since F(X) can be any implementation. Maybe it is more suitable to say: F(X): selected implementation depending on the real types of X F(Y): call to the selected implementation (?) Again, F(X) does not depend on Y... So I don't know exactly how the dispatching can better fit into the functor representation. I am more at ease with an "embedding" representation. The dispatcher tries to embed (express) the interface types (X) in a "specific" (template space) type (Y), for all the parameters. We are not interested in any possible mappings in the space of X or Y, but only in the points (elements) in both sides. I don't know if I was clear enough. What do you think? Do you think it might be relevant for boost?
-regards, Larry
Regards, Raffi
participants (4)
-
David Abrahams
-
Larry Evans
-
Mathias Gaunard
-
Raffi Enficiaud