Interest in runtime concepts library.

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Interest in runtime concepts library.

Germán Diago
Hi all. I'm implementing a framework to use runtime concepts for a
project of myself.
I would like to know the interest in that library. For now, I have a
proof of concept working.

The goal of the library, for now, is to be able to use objects of
differente types without
having any inheritance requirements and being nonintrusive. The code
has my own standards
for naming conventions, but that can be changed later when I have
something ready for
consumption.

Now, to the metal. First, an example. Imagine we have two types of
Devices. One of them
is a DeviceC (which stands for Device Concept) and another, more
refined one is PlugableDeviceC.

With my library I can write something like this to create types that
can hold classes that model both concepts:


//Base concept
class IDeviceC {
public:
        virtual std::size_t getCapacityInMB() const = 0;
};


template <class T>
class DeviceCModel : public MovableModelBase<T>, public IDeviceC {
public:
        IMPLEMENT_MODEL_DEFAULT_MEMBERS(DeviceCModel)

        virtual std::size_t getCapacityInMB() const {
                return this->getData().getCapacityInMB();
        }
};

//This is a DeviceC runtime concept
typedef MovableConceptBase<IDeviceC, DeviceCModel> DeviceC;



//Refined concept
class IPlugableDeviceC : public IDeviceC {
public:
        virtual void onOpen() = 0;
        virtual void onRemove() = 0;
};


template <class T>
class PlugableDeviceCModel : public IPlugableDeviceC, public
MovableModelBase<T> {
public:
        IMPLEMENT_MODEL_DEFAULT_MEMBERS(PlugableDeviceCModel)

        virtual void onOpen() {
                return this->getData().onOpen();
        }


        virtual std::size_t getCapacityInMB() const {
                return this->getData().getCapacityInMB();
        }

        virtual void onRemove() {
                return this->getData().onRemove();
        }
};


//Create concept class that refines DeviceC, The 3rd parameter is the
concept it refines
typedef MovableConceptBase<IPlugableDeviceC, PlugableDeviceCModel,
DeviceC> PlugableDeviceC;



Now we have two classes, DeviceC and PlugableDeviceC which can be used
to hold any type that models
that concept.

Now, in my main program I implemented two classes, one of them
modeling a PlugableDeviceC and another one
modeling DeviceC:



class LegacyDevice : NonCopyable {
public:
        //This class models DeviceC concept
        typedef DeviceC ModelOfType;

        std::size_t getCapacityInMB() const {
                return 10;
        }
};


class IpodDevice : NonCopyable {
public:
        //This class models PlugableDeviceC concept
        typedef PlugableDeviceC ModelOfType;

        void onOpen() {
                std::cout << "Opening Ipod device" << std::endl;
        }
        void onRemove() {}

        std::size_t getCapacityInMB() const {
                return 32 * 1024;
        }
};


operator-> is used to access the interface functions in an easy way. I
also coded a Ref<SomeConcept> type
which behaves like a C++ reference. It has implicit conversion to Base
concepts. You can also cast to different
concept types (more refined, like downcasting with inheritance). The
return of a casting with a concept as a parameter
always returns a Ref<SomeConcept>. You can use that reference with
operator-> to access its members (in C++
I can't overload operator. , so I used operator-> to delegate
functions). If you cast to a non-concept type (a concrete one)
it will return a regular c++ reference. The result is something like
this for now:

int main(int argc, char * argv[]) {
        std::vector<DeviceC> devices;
        IpodDevice ipod;
        LegacyDevice legacy_device;

        devices.push_back(move(ipod));
        devices.push_back(move(legacy_device));

        //getCapacityInMB() is a function that you can use for DeviceC
        for (auto & device : devices) {
                std::cout << device->getCapacityInMB() << std::endl;
        }

        /*This is a downcast. Although a class that models plugable device is being
         * held inside a DeviceC, you can downcast to its right (most-derived) concept
         */
        Ref<PlugableDeviceC> plugable_device = devices[0].castTo<PlugableDeviceC>();

        //Implicit conversion to base concept
        Ref<DeviceC> device = plugable_device;

        //This is a function that just exists for PlugableDeviceC types.
        plugable_device->onOpen();
}

There's still quite a bit of work to do:

-Implement a Ptr<Concept> that behaves like a reference but that accepts null.
-Make possible that a class models more than one concept.
-Lots of testing.
-Look at how runtime concepts behave with shared_ptrs to make a
sensible decision about
if a custom class like Ref should be implemented or a shared_ptr is good enough.


Opinions are welcome. The code is attached. Tested with g++ trunk in linux.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Concepts.tar.gz (250K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Germán Diago
>        Ref<PlugableDeviceC> plugable_device = devices[0].castTo<PlugableDeviceC>();



I improved a little my design and I got rid of Ref<Concept> and now normal
references can be used instead of them. The thing that doesn't work is
derived-to-base
conversion in shared_ptr, like this:


shared_ptr<BaseConcept> sptr(derived_concept_instance);

If I solve this very problem, I could make work every concept and its
refinements
in a very natural way.
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Daniel Larimer
In reply to this post by Germán Diago

On Feb 6, 2011, at 12:15 PM, Germán Diago wrote:

> Hi all. I'm implementing a framework to use runtime concepts for a
> project of myself.
> I would like to know the interest in that library. For now, I have a
> proof of concept working.
>
> The goal of the library, for now, is to be able to use objects of
> differente types without
> having any inheritance requirements and being nonintrusive.

It appears that your example is "intrusive" by requiring a typedef ModelOfType in the classes implementing your concept in order to perform the proper 'cast' whereas my implementation is 100% non-intrusive.

> The code
> has my own standards
> for naming conventions, but that can be changed later when I have
> something ready for
> consumption.


It appears that you and I are working on the same problem from two different angles.  I apologize for not reading your post before posting my own regarding [boost][interfaces].

It appears that your implementation requires more boilerplate code and provides no ability to define dynamic interfaces such as exposing any object that defines a "concept'" as a json-rpc server or create a json-rpc client based upon a "concept" definition.  

So my question to you, Germán Diago, in what ways is my solution undesirable for your application?  

To implement your example using "boost idl" you would do something like:

namespace idl_definition {
        struct DeviceC
        {
                 std::size_t getCapacityInMB() const;
        };
        struct IPlugableDeviceC : DeviceC
        {
                void onOpen();
                void onRemove();
        };
}

BOOST_IDL_INTERFACE( DeviceC,  (),  (getCapacityInMB) )
BOOST_IDL_INTERFACE( IPlugableDeviceC,  (IPlugableDeviceC),
         (onOpen)
         (onRemove) )

int main( int argc, char** argv )
{
        IpodDevice ipod;
        LegacyDevice legacy_device;
        std::vector<DeviceC> devices;
        devices.push_back( move(ipod) );
        devices.push_back( move(legacy_device) );
       
        IPlugableDeviceC  plugable_device = ???  I guess I need a way to 'down cast'
        devices[0].getCapacityInMB();
}

After attempting to implement your example using my API I realized that my solution currently does not provide a down-casting option while maintaining your syntax.  However down casting is possible like so:

int main( int argc, char** argv )
{
        IpodDevice ipod;
        LegacyDevice legacy_device;
        std::vector<DeviceC*> devices;
        devices.push_back( new IPluggableDeviceC(move(ipod)) );
        devices.push_back( new IPluggableDeviceC(move(legacy_device)) );

        devices[0].getCapacityInMB();
        IPlugableDeviceC*  plugable_device =  dynamic_cast<IPluggalbeDeviceC*>(devices[1]);
        pluggable_device->onOpen();
}

Additionally, my implementation supports public members as part of the 'Concept' which, as far as I can tell, is impossible with your design.

Dan  
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Germán Diago
> It appears that your example is "intrusive" by requiring a typedef ModelOfType in the classes implementing your concept in order to perform the proper 'cast' whereas my implementation is 100% non-intrusive.

No. It's not. You will be able to do it with a traits class. This
leaves it as 100% nonintrusive. And you will be able to adapt through
the model class the interface.
I mean, you can take a class that says getCapacity instead of
getCapacityInMB and you can adapt it through partial specialization.



> It appears that your implementation requires more boilerplate code and provides no ability to define dynamic interfaces such as exposing any object that defines a "concept'" as a json-rpc server or create a json-rpc client based upon a "concept" definition.

No. It wasn't a goal of my library at all. I must iterate one step at
a time. My goal is non-intrusive modeling of "interfaces", aka
concepts. Nothing more, nothing less.
I want it to play well with boost.concept_check, not a separate
library, but for now I'm just trying to make it work.


> So my question to you, Germán Diago, in what ways is my solution undesirable for your application?

I didn't know that your solution was targeting the same problem. I'll
keep an eye on it. My solution tries to be nonintrusive and as
lightweight as possible.
As I said, I didn't study yours.


> To implement your example using "boost idl" you would do something like:
>
> namespace idl_definition {
>        struct DeviceC
>        {
>                 std::size_t getCapacityInMB() const;
>        };
>        struct IPlugableDeviceC : DeviceC
>        {
>                void onOpen();
>                void onRemove();
>        };
> }
>
> BOOST_IDL_INTERFACE( DeviceC,  (),  (getCapacityInMB) )
> BOOST_IDL_INTERFACE( IPlugableDeviceC,  (IPlugableDeviceC),
>         (onOpen)
>         (onRemove) )

It's more or less the same boilerplate as in my class. A little more
in mine, but I use operator-> to access the interface. This way, if I
add two functions, I don't have
to replicate onOpen and onRemove in the macros. I don't use macros (at
least for now).


> int main( int argc, char** argv )
> {
>        IpodDevice ipod;
>        LegacyDevice legacy_device;
>        std::vector<DeviceC> devices;
>        devices.push_back( move(ipod) );
>        devices.push_back( move(legacy_device) );
>
>        IPlugableDeviceC  plugable_device = ???  I guess I need a way to 'down cast'
>        devices[0].getCapacityInMB();
> }
>
> After attempting to implement your example using my API I realized that my solution currently does not provide a down-casting option while maintaining your syntax.  However down casting is possible like so:

My ModelOfType was specifically targeted at solving the problem. When
a class is held in a DeviceC, if it models PlugableDeviceC, it will
instantiate the most-derived model, which
is the right way to do it (I think). This way you can downcast without problems


> int main( int argc, char** argv )
> {
>        IpodDevice ipod;
>        LegacyDevice legacy_device;
>        std::vector<DeviceC*> devices;
>        devices.push_back( new IPluggableDeviceC(move(ipod)) );
>        devices.push_back( new IPluggableDeviceC(move(legacy_device)) );
>
>        devices[0].getCapacityInMB();
>        IPlugableDeviceC*  plugable_device =  dynamic_cast<IPluggalbeDeviceC*>(devices[1]);
>        pluggable_device->onOpen();
> }
>
> Additionally, my implementation supports public members as part of the 'Concept' which, as far as I can tell, is impossible with your design.

I don't think it's a very important feature. I could consider to add
it later, but I think that member functions is more than enough.


> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Daniel Larimer

On Feb 7, 2011, at 8:43 AM, Germán Diago wrote:

>> It appears that your example is "intrusive" by requiring a typedef ModelOfType in the classes implementing your concept in order to perform the proper 'cast' whereas my implementation is 100% non-intrusive.
>
> No. It's not. You will be able to do it with a traits class. This
> leaves it as 100% nonintrusive. And you will be able to adapt through
> the model class the interface.
> I mean, you can take a class that says getCapacity instead of
> getCapacityInMB and you can adapt it through partial specialization.
>
>
>> It appears that your implementation requires more boilerplate code and provides no ability to define dynamic interfaces such as exposing any object that defines a "concept'" as a json-rpc server or create a json-rpc client based upon a "concept" definition.
>
> No. It wasn't a goal of my library at all. I must iterate one step at
> a time. My goal is non-intrusive modeling of "interfaces", aka
> concepts. Nothing more, nothing less.
> I want it to play well with boost.concept_check, not a separate
> library, but for now I'm just trying to make it work.
>
  I need to study boost concept check better, but I believe that concept checking was one of the goals of 6 year-old Boost Interface Library.   Or did you mean to say that you wanted boost concept check to validate your interfaces as conforming to the concept. There is a good possibility that my design (public function objects) would have more difficulty conforming to concepts than yours (public member functions).


>
>> So my question to you, Germán Diago, in what ways is my solution undesirable for your application?
>
> I didn't know that your solution was targeting the same problem. I'll
> keep an eye on it. My solution tries to be nonintrusive and as
> lightweight as possible.
> As I said, I didn't study yours.
>
My code is still a work in progress, I have checked my initial code in at https://github.com/bytemaster/boost_dev

I recently modified my interface to support custom transformations of the interface via a template parameter.  My idea was that I would implement a few initial transformation:
       
        mirror_interface       - exposes the un-modified interface.
        async_interface       - converts all return types into futures<return_type>
        const_interface        - only exposes const methods.



>
>> To implement your example using "boost idl" you would do something like:
>>
>> namespace idl_definition {
>>        struct DeviceC
>>        {
>>                 std::size_t getCapacityInMB() const;
>>        };
>>        struct IPlugableDeviceC : DeviceC
>>        {
>>                void onOpen();
>>                void onRemove();
>>        };
>> }
>>
>> BOOST_IDL_INTERFACE( DeviceC,  (),  (getCapacityInMB) )
>> BOOST_IDL_INTERFACE( IPlugableDeviceC,  (IPlugableDeviceC),
>>         (onOpen)
>>         (onRemove) )
>
> It's more or less the same boilerplate as in my class. A little more
> in mine, but I use operator-> to access the interface. This way, if I
> add two functions, I don't have
> to replicate onOpen and onRemove in the macros. I don't use macros (at
> least for now).

I don't like the use of macro's, but one of my design goals was to provide reflection on the interface and macro's are the best way to ensure names stringize properly and to reduce boiler plate code.

>
>
>> int main( int argc, char** argv )
>> {
>>        IpodDevice ipod;
>>        LegacyDevice legacy_device;
>>        std::vector<DeviceC> devices;
>>        devices.push_back( move(ipod) );
>>        devices.push_back( move(legacy_device) );
>>
>>        IPlugableDeviceC  plugable_device = ???  I guess I need a way to 'down cast'
>>        devices[0].getCapacityInMB();
>> }
>>
>> After attempting to implement your example using my API I realized that my solution currently does not provide a down-casting option while maintaining your syntax.  However down casting is possible like so:
>
> My ModelOfType was specifically targeted at solving the problem. When
> a class is held in a DeviceC, if it models PlugableDeviceC, it will
> instantiate the most-derived model, which
> is the right way to do it (I think). This way you can downcast without problems

Very good idea.  I did not get that from your original description.   Unfortunately, I do not think that can work with my approach because of the requirement to allocate space for all of the function objects.  So my approach certainly has the downside of requiring more memory to allocate 1 function object per in order to gain dynamic implementations of the interface and introspection.

>
>
>> int main( int argc, char** argv )
>> {
>>        IpodDevice ipod;
>>        LegacyDevice legacy_device;
>>        std::vector<DeviceC*> devices;
>>        devices.push_back( new IPluggableDeviceC(move(ipod)) );
>>        devices.push_back( new IPluggableDeviceC(move(legacy_device)) );
>>
>>        devices[0].getCapacityInMB();
>>        IPlugableDeviceC*  plugable_device =  dynamic_cast<IPluggalbeDeviceC*>(devices[1]);
>>        pluggable_device->onOpen();
>> }
>>
>> Additionally, my implementation supports public members as part of the 'Concept' which, as far as I can tell, is impossible with your design.
>
> I don't think it's a very important feature. I could consider to add
> it later, but I think that member functions is more than enough.

Obviously we have a different set of requirements.  My question becomes, do our requirements conflict with each  other or is there some overlap where we can work together for a more powerful/general solution.   I hope my previous e-mail was not interpreted as being critical of your code or too self-promoting of mine.  I am really just interested in feedback and to understand what people would want in such an interface and what design choices are 'deal breakers'.  I was encouraged to see your e-mail because it suggests that there is an independent need for some type of solution here.

For example, I could modify the code generated by the macro to make the interface class inherit base classes that implement the method name instead of using member function objects.  

>
>
>> _______________________________________________
>> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Germán Diago
>  I need to study boost concept check better, but I believe that concept checking was one of the goals of 6 year-old Boost Interface Library.   Or did you mean to say that you wanted boost concept check to validate your interfaces as conforming to the concept. There is a good possibility that my design (public function objects) would have more difficulty conforming to concepts than yours (public member functions).

If you are following the c++0x standarization process, you know that
there was a concepts proposal that finally went out of the standard.
This is what Boost.ConceptCheck tries to workaround as a library.
But there's a piece missing. Concepts are a compile-time feature. I
want that classes covering those compile-time concepts can be stored,
without knowing the type and without requiring inheritance.
So, the goal of my library is the runtime part of the compile-time
feature called concepts that were to go inside c++0x.


> I recently modified my interface to support custom transformations of the interface via a template parameter.  My idea was that I would implement a few initial transformation:
>
>        mirror_interface       - exposes the un-modified interface.
>        async_interface       - converts all return types into futures<return_type>
>        const_interface        - only exposes const methods.

I think that the goals of your library are broader than those of mine.
I just want that compile-time concepts can be held in a runtime
object. That's my primary goal now.

> I don't like the use of macro's, but one of my design goals was to provide reflection on the interface and macro's are the best way to ensure names stringize properly and to reduce boiler plate code.

Ok. There's no choice then. But reflection is not one of my goals for
now, so I did it without those macros.

> Obviously we have a different set of requirements.  My question becomes, do our requirements conflict with each  other or is there some overlap where we can work together for a more powerful/general solution.   I hope my previous e-mail was not interpreted as being critical of your code or too self-promoting of mine.  I am really just interested in feedback and to understand what people would want in such an interface and what design choices are 'deal breakers'.  I was encouraged to see your e-mail because it suggests that there is an independent need for some type of solution here.
>
> For example, I could modify the code generated by the macro to make the interface class inherit base classes that implement the method name instead of using member function objects.

I think your design, as I said, targets more features than mine. Think
of my library as an extension to get rid of object-oriented
programming. Instead, the goal is to be able to use concepts from
boost.conceptcheck and to be able to hold objects that represent those
concepts at runtime. After that, I want that these concept classes to
integrate with the language as well as I can.

I want them to play well with shared_ptr. Specifically, I would like
to be able to point with a shared_ptr<BaseConcept> to a
shared_ptr<RefinedConcept>. I want to be able to use c++ references
to point to concepts. I can already do it:

IpodDevice dev;
DeviceC my_device(move(dev)); //It's holding a PlugableDeviceC, in
reality, so I can downcast.

PlugableDeviceC & dev_ref = my_device.castTo<PlugableDeviceC>();


IpodDevice dev2;

PlugableDeviceC my_device2(move(dev2));

DeviceC & my_device2_ref = my_device2; //Automatic (and safe) implicit
conversion.


My goal is a library that integrates with generic programming giving a
runtime counterpart.
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Vicente Botet
In reply to this post by Germán Diago
Germán Diago wrote
Hi all. I'm implementing a framework to use runtime concepts for a
project of myself.
I would like to know the interest in that library. For now, I have a
proof of concept working.

The goal of the library, for now, is to be able to use objects of
differente types without
having any inheritance requirements and being nonintrusive. The code
has my own standards
for naming conventions, but that can be changed later when I have
something ready for
consumption.

<snip>

Opinions are welcome.
Hi,
It seems that what you are doing is providing some "Type Erasure" helper classes.

Maybe you will be interested in these presentations.
http://www.boostcon.com/community/wiki/show/private/2010/
# Type Erasure, Type Erasure Expression Templates Video

Best,
Vicente
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Germán Diago
> Hi,
> It seems that what you are doing is providing some "Type Erasure" helper
> classes.
>
> Maybe you will be interested in these presentations.
> http://www.boostcon.com/community/wiki/show/private/2010/
> # Type Erasure, Type Erasure Expression Templates Video

Thank you very much. I know those videos but I didn't watch them completely.
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Interest in runtime concepts library.

Daniel Larimer

On Feb 7, 2011, at 12:49 PM, Germán Diago wrote:

>> Hi,
>> It seems that what you are doing is providing some "Type Erasure" helper
>> classes.
>>
>> Maybe you will be interested in these presentations.
>> http://www.boostcon.com/community/wiki/show/private/2010/
>> # Type Erasure, Type Erasure Expression Templates Video
>
> Thank you very much. I know those videos but I didn't watch them completely.

Exactly like the presentation on type erasure.  So perhaps a better way of describing it is an automated means of defining type-erasures.  

In fact, I was implementing the core of the type in a manner very similar to boost::any.  

So what I was proposing is:
        Type Erasure  + Type Transformation + Type Reflection + Dynamic Type Implementation


> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost