fusion and forwarding problem

First of all a great thank-you (!!!) to Joel and the rest behind the development of fusion. It's solved so many tasks for me and made me write so little code the past months I probably shouldn't even get paid. database-abstraction, remote function invocation with async handlers, console/web interface invocation, etc.. It's changed my perception of effective programming to a complete new level. What before was a struggle with mpl, is now clean and easy. Ok, so to my humble problem. I'm constantly finding myself writing those overloaded functions like: ------------------ template<class A0, class A1, ....> void operator()(const A0& a0, const A1& a1, ...) const { do_something_very_clever(fusion_sequence_type(a0, a1, ...)); } ------------------ This is cumbersome and boring, and usually I just add the overloads when someone else needs it.. It's not always the case I can know beforehand to overload const T& or just T& also, but it depends on the use case. Anyways, my idea is to generate the correct overloaded operator()(...) by specializing on the size of the fusion sequence, instantiate the sequence with the parameters when invoked by client code, and then use CRTP downcast to invoke a function with the fusion sequence. This way I can do the job with my neat fusion sequence without client-code needing to mess with boost::fusion::tie and boost::ref etc, and I don't have to bother with the overloads. I guess an example would explain better than the above noise. Here's what a trivial multi-dispatch function would look like. I use function_types here to build my fusion sequence, but it could just as well have been some preprocessor enumeration dito instead. The overloading class selecting the overload is named to pack_args (attached). template<class Func> struct simple_signal : public pack_args < simple_signal const, typename boost::fusion::result_of::as_vector<boost::function_types::parameter_types<Func>
::type, void
{ typedef boost::function1<void, const sequence_type> function; template<class F> struct invoke { invoke(const F& f) : f_(f) {} void operator()(const sequence_type& seq) const { boost::fusion::unpack_args(f_, seq); } F f_; }; template<class F> void connect(const F& f) { m_functions.push_back(invoke<F>(f)); } // callback from pack_args. I agree this convention looks ugly, but it'll do for now.. void on_packed_args(const sequence_type& seq) const { for(std::list<function>::const_iterator i = m_functions.begin(); i != m_functions.end(); ++i) { (*i)(seq); } } private: std::list<function> m_functions; }; I can now use the above with simple_signal<void(int const, int const) > sig; sig(24, 54); // call all connected functions here.. If I have overlooked anything in fusion that would give me this functionality, please point me in the right direction and I'll be forever grateful :) Regards, Christian

Hi Christian, Christian Holmquist wrote:
Ok, so to my humble problem. I'm constantly finding myself writing those overloaded functions like:
------------------ template<class A0, class A1, ....> void operator()(const A0& a0, const A1& a1, ...) const { do_something_very_clever(fusion_sequence_type(a0, a1, ...)); } ------------------
This is cumbersome and boring, and usually I just add the overloads when someone else needs it.. It's not always the case I can know beforehand to overload const T& or just T& also, but it depends on the use case.
I mentioned this case in my review and already spent some time on implementing a solution. The second rewrite didn't satisfy me, so here's are the design sketches for the next attempt: A set of class templates with (at least) the following template parameters: 1. An unary (Fusion-) function object 2. Some way to specifiy the sequence implementation 3. Constraints that apply to the arguments of the (overloaded) call-operator The function object would describe a finalizing transform and do whatever you want to get done. The sequence implementation should be parametrized, to share the (possibly lengthy, see below) code. The constraints would be needed to specify things such as min/max arity or required argument types / conversion properties (some compile time functional, such as an MPL lambda expression would probably be the most flexibile). They're also needed to control early instantiation of the "empty-sequence path" (nullary operator() isn't lazy, because it can't be a template). There would be multiple variants of these class templates with different properties of the call operator: - only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie) - only accepting predefined types (like <seq>'s ctor, this one would have another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it) Note that 'make_<seq>' and '<seq>_tie' function could be implemented as global instance variables of the (appropriately configured) templates for the first two cases (the former with a copy and the latter with an identity function, both unrestricted). I don't want to have to do a fourth rewrite, so comments are very much welcome. Regards, Tobias

There would be multiple variants of these class templates with different
Tobias, Thanks you for the reply on this matter. Is your code available in the Boost Vault for download, and some example using it? properties of the call operator:
- only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie) - only accepting predefined types (like <seq>'s ctor, this one would
have
another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it)
I don't understand your reason for the lvalues/rvalues explotion. The distinct type is available in the fusion sequence, why overload completely unspecified types? (if that's what you do?). Given the sequence<int, int&, const int&> I would expect the overload to be (ignoring return type here) void operator()(int, int&, const int&). The utility I wrote solves this with void operator() ( typename fusion::result_of::value_at_c<Sequence, 0>::type, typename fusion::result_of::value_at_c<Sequence, 1>::type, typename fusion::result_of::value_at_c<Sequence, 2>::type ) I assume I've oversimplified the issue, so what aspect am I missing here? Thanks, Christian On 20/12/06, Tobias Schwinger <tschwinger@neoscientists.org> wrote:
Hi Christian,
Christian Holmquist wrote:
Ok, so to my humble problem. I'm constantly finding myself writing those overloaded functions like:
------------------ template<class A0, class A1, ....> void operator()(const A0& a0, const A1& a1, ...) const { do_something_very_clever(fusion_sequence_type(a0, a1, ...)); } ------------------
This is cumbersome and boring, and usually I just add the overloads when someone else needs it.. It's not always the case I can know beforehand to overload const T& or just T& also, but it depends on the use case.
I mentioned this case in my review and already spent some time on implementing a solution. The second rewrite didn't satisfy me, so here's are the design sketches for the next attempt:
A set of class templates with (at least) the following template parameters:
1. An unary (Fusion-) function object 2. Some way to specifiy the sequence implementation 3. Constraints that apply to the arguments of the (overloaded) call-operator
The function object would describe a finalizing transform and do whatever you want to get done. The sequence implementation should be parametrized, to share the (possibly lengthy, see below) code. The constraints would be needed to specify things such as min/max arity or required argument types / conversion properties (some compile time functional, such as an MPL lambda expression would probably be the most flexibile). They're also needed to control early instantiation of the "empty-sequence path" (nullary operator() isn't lazy, because it can't be a template).
There would be multiple variants of these class templates with different properties of the call operator:
- only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie) - only accepting predefined types (like <seq>'s ctor, this one would have another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it)
Note that 'make_<seq>' and '<seq>_tie' function could be implemented as global instance variables of the (appropriately configured) templates for the first two cases (the former with a copy and the latter with an identity function, both unrestricted).
I don't want to have to do a fourth rewrite, so comments are very much welcome.
Regards,
Tobias
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Please ignore my last post, I finally understood the meaning of what you have written. My problem is of more simple nature, so I was a bit lost in my own thoughts here.. I would be very interested in using your extension. Regards, Christian On 20/12/06, Christian Holmquist <c.holmquist@gmail.com> wrote:
Tobias, Thanks you for the reply on this matter.
Is your code available in the Boost Vault for download, and some example using it?
There would be multiple variants of these class templates with different properties of the call operator:
- only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie) - only accepting predefined types (like <seq>'s ctor, this one would have another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it)
I don't understand your reason for the lvalues/rvalues explotion. The distinct type is available in the fusion sequence, why overload completely unspecified types? (if that's what you do?). Given the sequence<int, int&, const int&> I would expect the overload to be (ignoring return type here) void operator()(int, int&, const int&). The utility I wrote solves this with
void operator() ( typename fusion::result_of::value_at_c<Sequence, 0>::type, typename fusion::result_of::value_at_c<Sequence, 1>::type, typename fusion::result_of::value_at_c<Sequence, 2>::type )
I assume I've oversimplified the issue, so what aspect am I missing here?
Thanks, Christian
On 20/12/06, Tobias Schwinger < tschwinger@neoscientists.org> wrote:
Hi Christian,
Ok, so to my humble problem. I'm constantly finding myself writing
Christian Holmquist wrote: those
overloaded functions like:
------------------ template<class A0, class A1, ....> void operator()(const A0& a0, const A1& a1, ...) const { do_something_very_clever(fusion_sequence_type(a0, a1, ...)); } ------------------
This is cumbersome and boring, and usually I just add the overloads when someone else needs it.. It's not always the case I can know beforehand to overload const T& or just T& also, but it depends on the use case.
I mentioned this case in my review and already spent some time on implementing a solution. The second rewrite didn't satisfy me, so here's are the design sketches for the next attempt:
A set of class templates with (at least) the following template parameters:
1. An unary (Fusion-) function object 2. Some way to specifiy the sequence implementation 3. Constraints that apply to the arguments of the (overloaded) call-operator
The function object would describe a finalizing transform and do whatever you want to get done. The sequence implementation should be parametrized, to share the (possibly lengthy, see below) code. The constraints would be needed to specify things such as min/max arity or required argument types / conversion properties (some compile time functional, such as an MPL lambda expression would probably be the most flexibile). They're also needed to control early instantiation of the "empty-sequence path" (nullary operator() isn't lazy, because it can't be a template).
There would be multiple variants of these class templates with different properties of the call operator:
- only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie) - only accepting predefined types (like <seq>'s ctor, this one would have another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it)
Note that 'make_<seq>' and '<seq>_tie' function could be implemented as global instance variables of the (appropriately configured) templates for the first two cases (the former with a copy and the latter with an identity function, both unrestricted).
I don't want to have to do a fourth rewrite, so comments are very much welcome.
Regards,
Tobias
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Christian Holmquist wrote:
Please ignore my last post, I finally understood the meaning of what you have written. My problem is of more simple nature, so I was a bit lost in my own thoughts here..
I just replied to it.
I would be very interested in using your extension.
Hey, that sounds motivating! Me too ;-). I'll get back to it - but I probably won't get anything done before the beginning of next week. Regards, Tobias

Tobias Schwinger wrote:
Christian Holmquist wrote:
Please ignore my last post, I finally understood the meaning of what you have written. My problem is of more simple nature, so I was a bit lost in my own thoughts here..
I just replied to it.
I would be very interested in using your extension.
Hey, that sounds motivating! Me too ;-). I'll get back to it - but I probably won't get anything done before the beginning of next week.
I'll add to that motivation: I'm interested too. I must admit though that I've had not enough time yet to peruse the code. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Tobias Schwinger wrote:
Christian Holmquist wrote:
I would be very interested in using your extension.
Hey, that sounds motivating! Me too ;-). I'll get back to it - but I probably won't get anything done before the beginning of next week.
I'll add to that motivation: I'm interested too. I must admit though that I've had not enough time yet to peruse the code.
Cool! Never mind that code: It's just an old example to partially demonstrate the power of the proposal - and you might have looked at it already when it was new. Regards, Tobias

Please ignore my last post, I finally understood the meaning of what you have written. My problem is of more simple nature, so I was a bit lost in my own thoughts here..
I just replied to it.
Your reply was very informative none the less, much appreciated. I'll try to play a bit with the code you supplied, thanks. On 21/12/06, Tobias Schwinger <tschwinger@neoscientists.org> wrote:
Christian Holmquist wrote:
Please ignore my last post, I finally understood the meaning of what you have written. My problem is of more simple nature, so I was a bit lost in my own thoughts here..
I just replied to it.
I would be very interested in using your extension.
Hey, that sounds motivating! Me too ;-). I'll get back to it - but I probably won't get anything done before the beginning of next week.
Regards,
Tobias
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Tobias Schwinger wrote:
Christian Holmquist wrote:
I would be very interested in using your extension.
Hey, that sounds motivating! Me too ;-). I'll get back to it - but I probably won't get anything done before the beginning of next week.
Here it is: http://tinyurl.com/ydoq9b (zip archive in the Boost file vault) The components depend on Boost.FunctionTypes: http://tinyurl.com/qaf5f (zip archive in the Boost file vault) The archive contains six components to transform and call functionals including unit tests. I'll attach the README file with a brief reference manual to this post as an appetizer. The features proposed earlier in this discussion have been implemented except parametrization of the sequence, because Fusion currently does not provide some kind of "tag-dispatched construction mechanism" (a metafunction that, given a sequence (or sequence tag) and some view, computes a sequence type with a constructor that accepts the types in the view as its arguments) which would be needed to do it right. Given the choice between making a very intrusive proposal or messing up my own code by making it inextensible, I decided that the feature wouldn't be worth it (just using fusion::vector for feeding fused functions, for now). fusion::unpack_args has been renamed to fusion::invoke, got some polishing and was moved to the functional folder (with the new components in place, that's clearly where it belongs). I also added a function wrapper that binds the first argument to fusion::invoke called fusion::fused. Thanks to João Abecasis at this point for writing fusion::unpack_args. Regards - and happy new year, Tobias Boost.Fusion functionals subsystem proposal. (C) Copyright João Abecasi and Tobias Schwinger Use modification and distribution are subject to the Boost Software License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> Requirements ============ The components within this archive depend on FunctionTypes, a Boost library that has not been added to the CVS yet. The archive function_types.zip is located in the Template Metaprogramming folder of the Boost file vault. http://boost-consulting.com/vault ......... Boost file vault http://tinyurl.com/qaf5f .................. FunctionTypes, download link Files ===== boost/fusion/functional/detail/access.hpp boost/fusion/functional/detail/gen_nullary_result_of_spec.hpp boost/fusion/functional/detail/has_type.hpp boost/fusion/functional/detail/nullary_call_base.hpp boost/fusion/functional/detail/pow2_explode.hpp boost/fusion/functional/detail/pt_def.hpp boost/fusion/functional/detail/pt_undef.hpp boost/fusion/functional/detail/that_ptr.hpp boost/fusion/functional/fused.hpp boost/fusion/functional/invoke.hpp boost/fusion/functional/limits.hpp boost/fusion/functional/unfused_generic.hpp boost/fusion/functional/unfused_lvalue_args.hpp boost/fusion/functional/unfused_rvalue_args.hpp boost/fusion/functional/unfused_typed.hpp libs/fusion/test/functional/fused.cpp libs/fusion/test/functional/invoke.cpp libs/fusion/test/functional/unfused_generic.cpp libs/fusion/test/functional/unfused_lvalue_args.cpp libs/fusion/test/functional/unfused_rvalue_args.cpp libs/fusion/test/functional/unfused_typed.cpp libs/fusion/test/Jamfile libs/fusion/test/Jamfile.patch README_FUSION_FUNCTIONAL.TXT Quick Reference =============== Concepts Regular Function defines the superset of builtin functionals (such as function pointers) and function objects that work with boost::result_of. Components template< class Function > class unfused_generic; template< class Function > class unfused_lvalue_args; template< class Function > class unfused_rvalue_args; Transform Polymorphic Function Objects that take a single Sequence into Regular Functions that accept a variable number of generic arguments. Restrictions can be applied by removing the type member from the nested result metafunction (in this case SFINAE applies for non-nullary calls - the nullary call is taken care of by the library, be aware that the nullary call operator can't be a template and thus results in earlier instantiation, however). The rvalue and lvalue variants only accept const and non-const references respectively. template< class Function, class Sequence > class unfused_typed; Transform Polymorphic Function Objects that take a single Sequence into Regular Functions that accept a variable number of strict typed arguments. The second template parameter specifies the types in form of a Fusion sequence. Restrictions can be applied by removing the type member from the nested result metafunction (in this case SFINAE applies for non-nullary calls - the nullary call is taken care of by the library, be aware that the nullary call operator can't be a template and thus results in earlier instantiation, however). template< typename Function > class fused; Transforms a Regular Function into a Polymorphic Function Object that takes a Sequence with the arguments for the transformed function. The Function template parameter may be const or a reference. Const qualification is preserved and propagated appropriately. namespace result_of { template< typename Function, class Sequence > struct invoke; } template< typename Function, class Sequence > inline typename result_of::invoke<Function, Sequence>::type invoke(Function, Sequence &); template< typename Function, class Sequence > inline typename result_of::invoke<Function, Sequence const>::type invoke(Function, Sequence const &); Calls a Regular Function with the arguments from a Sequence. The first template parameter can be specialized explicitly to avoid copying and to control the const qualification of the function.

Christian Holmquist wrote:
Tobias, Thanks you for the reply on this matter.
Is your code available in the Boost Vault for download, and some example using it?
There used to be an early stage of both in the vault, but I took it offline once it was clear to me that it was still very much insufficient. I can only find the example code ITM and will attach it to this post.
There would be multiple variants of these class templates with different properties of the call operator:
- only accepting lvalues (like make_<seq>) - only accepting rvalues (like <seq>_tie)
Sorry, here's a typo - <seq>_tie binds lvalues and make_<seq> binds to rvalues, of course.
- only accepting predefined types (like <seq>'s ctor, this one would have another template parameter) - accepting any combination of lvalues and rvalues (exploding all 2^N possibilities - a brute force solution to the forwarding problem, until we have better ways to solve it)
I don't understand your reason for the lvalues/rvalues explotion. The distinct type is available in the fusion sequence, why overload completely unspecified types? (if that's what you do?).
<quote from your original post> template<class A0, class A1, ....> void operator()(const A0& a0, const A1& a1, ...) const { do_something_very_clever(fusion_sequence_type(a0, a1, ...)); } </quote from your original post> Here we don't know the template argument types, do we? It's the first case I mentioned (the most interesting cases are those where 'fusion_sequence_type' isn't known before operator() is instantiated). Although (almost) everything binds to "T const &" we can't know whether it was a temporary, so we can't safely store the refernce - it might start dangling. BTW: The result type is 'void'. If we want to factor this code out into some generic utility it's probably a wise choice to allow different results. The second case I mentioned is: template<class A0, class A1, ....> whatever operator()(A0& a0, A1& a1, ...) const; Now temporary arguments are not allowed at all (which can be very impractical of an interface) - a "pure tie". The fourth case lets us detect whether something is not a temporary, which can be a very valuable thing, especially if we want to defer function invocations. For an in-depth discussion of the issue see http://tinyurl.com/6enrw - The Forwarding Problem - Dimov, Hinnant, Abrahams
Given the sequence<int, int&, const int&> I would expect the overload to be (ignoring return type here) void operator()(int, int&, const int&).
In this case we are talking about the third case (the one where we know the types).
I assume I've oversimplified the issue, so what aspect am I missing here?
Never mind - I did so two times. I'm talking about how things should be done and not about I did them, after all. Regards, Tobias // (C) Copyright Tobias Schwinger // // Use modification and distribution are subject to the boost Software License, // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). //------------------------------------------------------------------------------ // // A simple bind implementation (compiles with msvc >= 7.1) // #include <typeinfo> #include <iostream> #include <boost/fusion/sequence/container/vector/vector.hpp> #include <boost/fusion/sequence/intrinsic/front.hpp> #include <boost/fusion/sequence/intrinsic/value_at.hpp> #include <boost/fusion/sequence/intrinsic/at.hpp> #include <boost/fusion/sequence/utility/unpack_args.hpp> #include <boost/fusion/algorithm/transformation/transform.hpp> #include <boost/fusion/algorithm/transformation/pop_front.hpp> // "case 1" from the post NOTE: NOT PART OF FUSION #include <boost/fusion/sequence/generation/vector_generator.hpp> // this header defines an experimental component that allows to specify the // result of a Fusion function object with an MPL lambda expression // NOTE: NOT PART OF FUSION #include <boost/fusion/support/lambda_result.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/bool.hpp> #include <boost/type_traits/add_const.hpp> #include <boost/type_traits/add_reference.hpp> // needed to workaround unpack_args bug #include <boost/fusion/sequence/conversion/as_vector.hpp> // note: another bug in unpack_args keeps this binder from working with // non-class functors. namespace impl { // note: one character identifiers keep qualified names short namespace r = boost::fusion; // r for runtime code namespace m = boost::fusion::result_of; // m for metafunctions namespace mpl = boost::mpl; using namespace mpl::placeholders; // use runtime code by default, using namespace r; // metafunctions unlikely to clash can go here using boost::add_const; using boost::add_reference; // placeholder template template<int I> struct placeholder : mpl::int_<I> { }; // is T the type of a placeholder? template<typename T> struct is_placeholder : mpl::false_ { }; template<int I> struct is_placeholder< placeholder<I> > : mpl::true_ { }; // runtime code for the transform operation template<typename Args> class transform_op : public lambda_result < mpl::if_< is_placeholder<_2>, m::at< add_const<_1>, _2> , add_reference< add_const<_2> > > > ::template closure<Args> { Args const & ref_args; public: explicit transform_op(Args const & args) : ref_args(args) { } // return bound argument template<typename BindingArg> inline BindingArg const & operator()(BindingArg const & a) const { return a; } // substitute placeholders with elements from the arguments tuple template<int Index> inline typename m::at_c<Args const, Index>::type operator()(placeholder<Index> const &) const { return at_c<Index>(this->ref_args); } }; // bound_function's operator() contains the argument transformation and // invokes the function template<class Binding> class bound_function : public lambda_result < m::unpack_args< m::value_at< add_const<_1> , mpl::int_<0>::type> , m::as_vector< // <-- workaround for unpack_args bug m::transform< m::pop_front< add_const<_1> > , transform_op<_2> > > // <-- workaround for unpack_args bug > > ::template closure<Binding> { Binding tpl_binding; public: explicit bound_function(Binding const & binding) : tpl_binding(binding) { } template<typename Args> inline typename result<Args>::type operator()(Args const & args) const { return unpack_args( front(this->tpl_binding) , as_vector( // <-- workaround for unpack_args bug transform( pop_front(this->tpl_binding) , transform_op<Args>(args) ) ) // <-- workaround for unpack_args bug ); } typedef bound_function type; // we leave this out for the sake of simplicity -- a full bind is not // possible... typedef mpl::false_ accepts_nullary_sequence; }; // vector_generator is used to create a bound_function struct bind_op : lambda_result< vector_generator< bound_function<_> > > { template<typename Binding> inline typename result<Binding>::type operator()(Binding const & binding) const { return typename result<Binding>::type(bound_function<Binding>(binding)); } }; typedef vector_generator<bind_op, mpl::false_> binder; } // placeholder globals impl::placeholder<0> const _1_ = impl::placeholder<0>(); impl::placeholder<1> const _2_ = impl::placeholder<1>(); impl::placeholder<2> const _3_ = impl::placeholder<2>(); impl::placeholder<3> const _4_ = impl::placeholder<3>(); // the binder is a global variable with an overloaded call operator impl::binder const bind = impl::binder(); //// example code struct func { typedef int result_type; inline int operator()(char const * greeting, char const * name) const { std::cout << greeting << ' ' << name << '!' << std::endl; return 77; } }; int main() { func f; bind(f,"Hello",_1_)("Phoenix"); return 0; }

Christian Holmquist wrote:
First of all a great thank-you (!!!) to Joel and the rest behind the development of fusion. It's solved so many tasks for me and made me write so little code the past months I probably shouldn't even get paid. database-abstraction, remote function invocation with async handlers, console/web interface invocation, etc.. It's changed my perception of effective programming to a complete new level. What before was a struggle with mpl, is now clean and easy.
Thank you very much, Christian. Well, it seems that Tobias is doing some progress on the matter. I'll try to sync up to what you've been discussing. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net
participants (4)
-
Christian Holmquist
-
Joel de Guzman
-
Tobias Schwinger
-
Tobias Schwinger