Anyone interested in a virtual constructor lib?

As a byproduct of my current work I factored out a small utility library that implements a virtual constructor. The implementation is based on boost::any and typeid(...).name(). It features dynamic creation of objects by name (string), and allows for default and custom constructors beeing called. It could be used as the basic step to object deserialization from a file, creation of operating system dependent implementations, or allow a library to instantiate user-pluggable classes. The design is inspired by the Io_obj example from Stroustrup's book on C++ programming. A small example to show the usage: #include <string> #include <iostream> #include "dynanew.hpp" using namespace dynanew; // a simple class hierarchy with abstact base class struct shape { virtual void draw() = 0; }; struct square : public shape { virtual void draw() { std::cout << "I am a square" << std::endl; } }; struct circle : public shape { circle() : radius(0) {} circle(double dr) : radius(dr) {} virtual void draw() { std::cout << "I am a circle of radius: " << radius << std::endl; } double radius; }; // create the type map TYPE_MAP(shape); // square with default constructor TYPE_REGISTER(shape, square); // circle with custom constructor taking a double parameter TYPE_REGISTER1(shape, circle, double); // circle with default constructor TYPE_REGISTER(shape, circle); int main(int argc, char* argv[]) { std::string type; type = "square"; shape* ps1 = dynamic_new<shape>(type); shape* ps2 = dynamic_new<shape>("circle", 20.0); shape* ps3 = dynamic_new<shape>("circle"); shape* ps4 = dynamic_new<shape>("square", 10.0); if (ps1) ps1->draw(); // prints: I am a square if (ps2) ps2->draw(); // prints: I am a circle of radius: 20.0 if (ps3) ps3->draw(); // prints: I am a circle of radius: 0.0 if (ps4) ps4->draw(); // does nothing since no ctor available delete ps1; delete ps2; delete ps3; delete ps4; return 0; } regards, Roland

Yes, I am interested in it. It appears to provide a way to do reflection on a limited basis by using a name to create an object. Hajo Kirchhoff in his LitWindow library also provides a way to do reflection based on names although I do not believe he has the ability which you have to dynamically create objects based on names. The general ideas that you both use, providing a way to register information about types, seems similar. I am interested, more for the ideas involved than for current practical use, in any general mechanism which provides reflection capabilities, where a name can be used to find out type information or variable information at run-time, or where an object can be created based on its name at run-time. The only nit I have to pick is calling your implementation a virtual constructor lib. I do not see where polymorphism comes into play in the actual construction of the objects from its name. Perhaps a "name constructor lib" would be more accurate. Roland Schwarz wrote:

Edward Diener wrote:
Yes I do. The Lit Window Library allows this. // get the type definition for the class foobar prop_t aType = get_prop_type_by_name("foobar"); // create an object on the heap accessor a = create_object(aType); // access member variable foobar.m_string cout << a.get_aggregate()["m_string"] << endl; // cast the generic accessor to a typed accessor typed_accessor<foobar> fbar=dynamic_cast_accessor<foobar>(a); assert(fbar.is_valid()); // copy the contents of the object to a different object foobar someObject; fbar.get(someObject); // destroy the object destroy_object(a); // a.destroy() is an alternative way of destroying it BTW, the base part of the lit window library does not contain any GUI code at all and is meant to be used independently for any kind of generic programming.
http://www.litwindow.com/library does this. The documentation is certainly not yet up to par, but the functionality is there and is pretty mature already, except for container support. This is still pretty spotty for any container other than stl::vector. Regards and I'd love to hear any comments on my code. Hajo

Hajo Kirchhoff wrote:
I am interested in the data accessor part of your library from the end-user perspective so I would encourage you to document that fully. I understand that you are using that functionality for your GUI library but, as you have discovered through packaging it as a base library, it holds general interest and functionality outside of its use in your GUI environment for those who are interested in a run-time reflection mechanism.

Edward Diener wrote:
I am interested in the data accessor part of your library from the end-user perspective so I would encourage you to document that fully. I understand
I will finish the documentation asap. In the mean time you might want to read this new introductory article into data adapters: http://www.litwindow.com/Library/Articles/dataabstractionlayer.htm Best regards and thank you for your interest in the library Hajo Kirchhoff

Edward Diener wrote:
Yes, I am interested in it. It appears to provide a way to do reflection on a limited basis by using a name to create an object.
I am not sure if this really compares to "reflection" (and introspection?). I am simply using a map std::map<std::string, boost::any >, where the any part holds a pointer to a constructor function. The string on the other side is a composite of the class name and typeid(FN).name(), where FN is the signature of the ctor function. If you think using the RTTI of C++ == reflection then your assumptions are correct.
I was not aware of this. Thank you for the pointer.
Hmm, I was already considering this, but look at it a different way: What is the sense of beeing able to construct an object of which you do not know anything? You won't be able to call a single function! So you definitely need at least an abstract base class that defines the common interfaces. And this is where polymorphism comes into play. I would even say that the "covariant return types" are at the core of my implementation. Now my implementation allows to instantiate any derived class. This is why it is called "virtual constructor". Also it is not me who coined this wording. As I already mentioned I was reading Stroustrups book and his explanation of the term "virtual constructor" inspired me to my implementation. (But having it called dynamic_new reflects your concerns a little I think.) Roland

On Sun, 05 Dec 2004 15:57:35 +0100, Roland Schwarz wrote
I'm interested in seeing something like this in boost... Question -- will this compile in your implementation? square* ps4 = dynamic_new<square>("square", 10.0); as well as: shape* ps4 = dynamic_new<shape>("square", 10.0); Jeff

Jeff Garland wrote:
Well this depends on which types you have registered. In square* ps4 = dynamic_new<square>("square", 10.0); you are assuming a different base class than shape, and to this end you also would need to "declare" TYPE_MAP(square); too. But I admit since I only posted a small example to find out the level of interest I did not put much effort to explain my rationale and the possible range of usages. It goes something along this: To call a function of an object you need its interface. I use an (not necessarily) abstract base class to describe it. I use the covariant return types feature of C++ to return pointers to derived classes from a creator function. So the pattern of usage is: baseclass* p = dynamic_new<baseclass>("derivedclass"); if the dynamic new cannot perform the requested operation at runtime it return a 0 - pointer instead. If succesfull p points to the derived class. To make it the exakt derived class, you need to cast it of course. derivedclass* p = (derivedclass*)dynamic_new<baseclass>("derivedclass"); But this of course is not of much use, since it is easier then to call derivedclass* p = new derivedclass; since you already know everything that is needed to create the object. Roland

On Mon, 06 Dec 2004 08:41:40 +0100, Roland Schwarz wrote
Ok.
I was asking because I was curious if the construction idiom based on string could be applied consistently throughout an application even if in some places in the code I had exact knowledge and hence wouldn't be constructing a base class pointer. My interpretation of the above is that in this case I have to drop back to doing a regular constructor because the virtual constructor doesn't handle this elegantly.... Jeff

Hi Roland,
As a byproduct of my current work I factored out a small utility library that implements a virtual constructor.
What if I write: shape* ps2 = dynamic_new<shape>("circle", 20); ? I tried to create virtual construct functionality for a future Boost.Plugin library and it worked like this: namespace boost { namespace plugin { template<> struct virtual_constructors<Weapon> { typedef mpl::list<mpl::list<std::string>, mpl::list<std::string, int> > type; }; }} Given those declaration, instantiaton of boost::plugin_factory<Weapon> will have a 'get' method for each listed constructor signature, and usual overload resolution will work. The disadvantage is that you need to explicitly instantiate 'virtual_construct', but then you get static checking. For example, a plugin can't forget to define a required constructor. I also considered this syntax: struct virtual_constructors<Weapon> { typedef mpl::list<ctor (std::string), ctor (std::string, int)> ..... where 'ctor' is auxillary struct, but maybe it's too smart, haven't decided yet. What would you say? - Volodya

Vladimir Prus wrote:
Since I am using a template function as a type generator and tipeid().name() in turn this will deduce a function requiring an int for the ctor. Since only double has been registered in the example program your example will not work. (It will give a 0 -pointer at runtime.) Please note that my implementation is not very smart and could be improved by someone more knowledgeable than me. Perhaps it is not so hard to add support for automatic type conversion too? But then my implementation fits entirely (almost) on two sheets of paper.
Altough I have not a very good knowledge of the mpl lib right now, this looks very interseting to me.
The disadvantage is that you need to explicitly instantiate 'virtual_construct',
Hmm, I cannot see how I ever could do away with this instatiation. Or do you mean it should better be done implicitely?
I am not entirely sure, if you intend this struct as external to your class or as a member? Most likely I did not yet get the full picture and my question might seem somewhat blurry. Could you post a little example that exhibits the user interface? I have the vague feeling that you have found a more pleasing way to specify the virtual constructors. Having said this I should mention another design criterion of mine: I wanted the mechansim to be non-intrusive and appliable to any class (as I think yours is too, isn't it?) Hidden behind my macros are objects whose sole purpose is that their (global) ctors are to be called before program start to register all the ctor types. E.g.: #define TYPE_REGISTER(B, D) dynanew::type_entry<B, D> type_entry_##D(#D) If there is anything smarter possible I would prefer it! BTW.: I am not so much interested in getting my proposal into boost as seeing this kind of functionality within boost. If you already are on the way to preparation of such a proposal I would in no way contend, rather support your efforts. If you think my implementation could be of interest to you I am pleased to mail you a copy for further study. Roland

Roland Schwarz wrote:
Yes, that's what I though. In other words, your implementation is dynamically checked. (This is consistent with the name 'dynamic_new', BTW).
The problem is that need either: 1. A statically available list of overloaded functions, so that compiler can do the overload resolution. 2. Boost.Overload library which can do it using typeid or some other metedata. Such library does not exist, though :-(
But then my implementation fits entirely (almost) on two sheets of paper.
Mine too ;-) template<class BasePlugin> struct virtual_constructors { typedef mpl::list<mpl::list<> > type; };
Well, I mean that unlike your solution, you can query for specific constructor signature. Using plugins as example, even if specific plugin class has additional constructors, it's not possible to call them via generic interface.
External template that the user has to specialize.
Sure. I'll expand the previous example. You have a class which is intended to be "base" for plugins. So, you can load dynamic library, and ask it to create a class, giving name (string) and a set of parameter. You can back a pointer to "base". Here's definition of the base class: class Weapon { public: virtual void fire() = 0; virtual ~Weapon() {} }; Second, it's needed to define a set of constructor parameter that each plugin should support: namespace boost { namespace plugin { template<> struct virtual_constructors<Weapon> { typedef mpl::list<mpl::list<std::string>, mpl::list<std::string, int> > type; }; The machinery used to create plugins is somewhat complex. I can describe the logic used for plugins, but to save space will describe only part which is usable everywhere. A class can be registred with: std::map<std::string, boost::any> classes; static boost::plugin::concrete_factory<Weapon, Missile> cf; boost::plugin::abstract_factory<Weapon>* w = &cf; classes.insert(std::make_pair("missile", w)); The 'abstract_factory' has a virtual 'create' method for each signature declared by 'virtual_constructors'. The 'concrete_factory' class defines those methods for a specific class to be created. The above map can be used as: // Want to create a weapon by name std::any f = classes["missile"]; any_cast<abstract_factory<Weapon>* >(f)->create("foo", 10); The use of any allows to store completely different factories in one map, and any_cast will verify that you're using the correct type. In Boost.Plugins the above code is hidden under higher-level interface, like: BOOST_EXPORT_PLUGIN(Weapon, Missile, "Missile"); BOOST_EXPORT_PLUGIN_LIST(); but it's just for convenience. - Volodya

Vladimir Prus wrote:
Yes this is by intent. This way it is possible to prepare for ctors that are not even available at compile time. A user might later decide to (dynamically) hook in a respective class.
Are you sure? Won't the upcoming typeof() library be of help here?
I am not sure if I can follow you there. 1) I think querying for specific constructor signature is exactly what I am doing. 2) Calling additional constructors that are not in the base class also is what I am doing. Or do you mean you want to explore an arbitrary class for it's available set of ctors at runtime before calling into them? I cannot see then what is the value of this? This surely would be of interest when trying to bind the classes to a scripting language. But this is not in the scope of my proposal. [...] Having glanced over your description I think there is some similarity (at least at the creation part). I would be very much interested in your boost::plugin lib! Unfortunately I was not able to find it in the boost or boost-sandbox so far. Can I download it somewhere to experiment with it? Roland

Roland Schwarz wrote:
Understood.
I think it just emulates in-language typeof, and does not do overload resolution.
Yes, I mean that your solution can query for a specific signature. Mine cannot. That can be considered disadvantage of my solution.
I did not consider this too.
I've just added it to sandbox: boost/plugin and libs/plugin. So far, the example only works for gcc/Linux, but otherwise should be OK. Comments are very appreciated. - Volodya
participants (5)
-
Edward Diener
-
Hajo Kirchhoff
-
Jeff Garland
-
Roland Schwarz
-
Vladimir Prus