Interest in runtime concepts library.

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.

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.

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

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

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

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.

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 -- View this message in context: http://boost.2283326.n4.nabble.com/Interest-in-runtime-concepts-library-tp32... Sent from the Boost - Dev mailing list archive at Nabble.com.

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.

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
participants (3)
-
Daniel Larimer
-
Germán Diago
-
Vicente Botet