Boost logo

Boost :

Subject: Re: [boost] [Boost-interest] C++ library for runtime-concepts (type-erasure)
From: Daniel Larimer (dlarimer_at_[hidden])
Date: 2011-02-15 15:38:08


I think I may have a solution for defining erasures with the total "cost" of a virtual method invocation and the
memory model of boost::any.

DEFINE_ERASURE( Calculator, (add)(sub),
        virtual double add(double) = 0;
        virtual double sub(double) = 0;
 )

Details are provided in the discussion below.

On Feb 15, 2011, at 11:58 AM, Matus Chochlik wrote:

> On Tue, Feb 15, 2011 at 3:34 PM, Daniel Larimer <dlarimer_at_[hidden]> wrote:
>> On 2/15/11 4:58 AM, "Germán Diago" <germandiago_at_[hidden]> wrote:
>>
>>>> I'm not sure if I understand correctly what you mean but if
>>>> you are referring to transforming something like this...
>>>>
>>>> struct some_type
>>>> {
>>>> T1 m1;
>>>> T2 m2;
>>>> ...
>>>> Tn mn;
>>>> };
>>>>
>>>
>>> That was a goal of Boost.IDL interfaces, but I tried to make a summary of
>>> what would be useful to take into account to design things that don't overlap.
>>>
>>>
>>>>
>>>> then it will be doable with Mirror (it actually already is
>>>> in the old version and partially also in the new one
>>>> but still undocumented).
>>
>> Color me very interested in how you achieved this. Though what I would
>> really like to see is:
>
> Basically the Mirror's registering macros create the following
> two helper structures for every member variable:
>
> template <typename X>
> struct _by_name_typ
> {
> typedef X member_variable_name;
> };
>
> template <typename X>
> struct _by_name_val
> {
> X member_variable_name;
> };

That was essentially my approach in Boost.IDL (New name suggestions?)

>
> then Mirror let's you choose the type(s) for X and compose them into
> a single class via multiple inheritance. Currently only the 'by_name'
> template class uses this to allow you to get access to member
> variable meta-data via its name at compile time (although I could
> not find too many use cases where this is really necessary to do :))
>
>>
>> struct some_type
>> {
>> std::string append( std::string, double );
>> };
>>
>> Converted Into functionally equivalent =>
>>
>> struct new_some_type
>> {
>> future<string> append( string, double )
>> {
>> future<string> fut;
>> m_socket->async_sendto( serialize( s, d ),
>> host, boost::bind(wait_for_response, fut );
>> return fut;
>> }
>> };
>
> This is doable. Mirror does not implement it yet but
> during the work on my master's thesis I've implemented
> a simple RPC mechanism that did something very
> similar to the above using reflection.
>

Doable you say.. Perhaps, but at what cost? Unlike member variables, there are a huge number of permutations that must be supported for each name that you wish to consider.

template<typename Signature, bool IsConst=false, bool IsVirtual=false, bool isStatic=false>
struct _by_name_method{}

template<typename OnClass, RTN(ARG1,...)>
struct _by_name_method : virtual public OnClass {
        RTN some_name(ARG1,...) { return static_cast<OnClass*>(this)->impl->some_name(ARG1,...) }
}

Then you run into the problem of specifying the implementation of each method in terms of the object they are being composed on to. In the example above I show an example using virtual inheritance and require that OnClass provide impl->name(args), this effectively just moved our problem.

Applied to type erasure:

class TypeErasure
{
        public:
                template<typename ModelImpl>
                TypeErasure( ModelImpl m )
                {
                        impl = new Model<ModelImpl>();
                }

                RTN some_name(ARGS...) { impl->some_name(); }

        private:
                struct Concept {
                        virtual RTN some_name(ARGS...) = 0;
                };
                template<typename M>
                struct Model : public M, public Concept {};
};

You need to define the method some_name() two different times, once in the concept and once in the TypeErasure.

You would need to do something like this:
template<typename VirtualBase, typename RTN(ARG1,...)>
struct _by_name_method : VirtualBase {
        RTN some_name(ARG1,...)
        {
                return ((VirtualBase*)this)->some_name(args...);
        }
}

So I suppose that if you did not mind creating 4 x (MAX PARAM) template specializations PER NAME to allow for const/nonconst virtual/nonvirtual overrides, then it would be ok. I guess, with veridic templates (which I am not familiar with due to lack of compiler support), this whole issue might go away.

In my alternative of converting methods into function<Signature> member variables, this "implementation" is dynamically provided by generating a suitable function object in the visitor.

I guess with the approach outlined above you could achieve the following "automatic TypeErasure" generation:

DEFINE_ERASURE( Calculator, (add)(sub),
        virtual double add(double) = 0;
        virtual double sub(double) = 0;
 )

Implemented as something like:

#define DEFINE_ERASURE( NAME, METHOD_SEQ, ... )
namespace detail { namespace NAME { namespace methods {
        SEQ_FOR_EACH( DEFINE_BY_NAME_METHOD )
}
        struct Concept {
                __VA_ARGS__
        };
} }
class NAME :
        SEQ_FOR_EACH( INHERIT_BY_NAME_METHOD )
{
        public:
                template<typename CONCEPT>
                NAME( CONCEPT c )
                :impl( new Model<CONCEPT>(c) ){}
                
        protected:
                scoped_ptr<Concept> impl;
        private:
                template<typename M>
                struct Model : public M, public detail::NAME::Concept {}
};

To improve performance on non-variadic template supporting compilers, you could specify the number of parameters like so:
DEFINE_ERASURE( Calculator, ((add,1))((sub,3)),
        virtual double add(double) = 0;
        virtual double sub(double,int,string) = 0;
 )

So my quick reply turned into a brainstorm dump. Hopefully the ideas are useful. I would like feedback on how such an implementation would be received by the community.

I suspect that this would be the most efficient solution, but it would still fail to provide some of the benefits of my member function object solution, specifically, generating RPC client stubs or scripting engine hooks or any other task that requires "per-method" data.

The method also forces you to list every method twice, once in the erasure and once in your type that implements the erasure. In situations such as building an RPC server, you only want to define the class once, then expose it with as little redundant information as possible. So I suspect that there is probably room for both types of erasure.

>>
>> Or converted Into =>
>>
>> struct some_new_type
>> {
>> function<string(string,double)> append;
>> };
>
> Hmm, I've never considered turning a function into a member
> variable but from the top of my head I don't see why this would
> not work by using some variation of the approach described
> above (well maybe overloads could cause some trouble...)
>
>>
>> Using just your Mirror mirror macros + templates?
>>
>> I am very impressed with Mirror, but its dependency on C++0x features that
>> are not available in MSVC is almost a non-starter for me. Particularly
>> because even though gcc supports much of C++0x, Mac OS X Development Tools
>> still comes bundled with gcc 4.2.1 which does not support ANY C++0x.
>
> Thank you and,
> yes, this is a problem, but I hope that other compilers besides GCC
> will implement C++0x after the final vote in March, many are already
> working on it. Anyway I believe the transition from C++98 to C++0x was
> worth it. In pre-C++0x I've spent more time struggling with the compilers
> to do basic things that now work perfectly.
>
> I do a lot of Windows programming myself and in projects using
> Mirror I'm stuck with Cygwin so getting Mirror going in MSVC is
> also one of my priorities.
>
>>
>> If you can show an example of how mirror would accomplish that, I will have
>> to reconsider my approach to Type Erasure, RPC, and serialization.
>>
>> What is the status of Mirror's review for inclusion in official Boost?
>
> There are several things currently preventing me from submitting
> Mirror for review:
>
> 1) As already discussed above it is not (yet) portable because of
> the lack of support for C++0x by the compilers
>
> 2) Expectation of breaking changes. I'm not going to submit the library
> for review until I'm at least 95 percent sure that there will be no breaking
> changes in the existing things. Now I'm somewhere around 70% :)
>
> 3) Few things to finish (like the manipulator generator mentioned before,
> the transformations discussed above and some others)
>
> 4) Documentation
>
> 5) Missing unit-tests
>
> As for a specific date, I've already made so many promises
> to various people about the submission for review that even
> I consider myself a liar now :) so I've stopped doing that.
>
> BR,
>
> Matus
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk