Have you ever written some code in Delphi or C++Builder? If yes, you might have
encountered TClass or TMetaClass types. TMetaClass is a class of which this
pointer points to the vtable. And TClass is a reference(pointer) to
TMetaClass.
Anyway, the conception of classes to be objects is very nice. For Smalltalk
programmers it's nothing new, but in C++, which is not pure object language,
this is something interesting.
But the main question is, what it can be used for. Well, mostly for creating
objects of unknown type for creator. You just have to pass the type variable
during run-time, and the function can create many instances of that
objects.
Note that it is essential that a type variable has to guarantee that the objects
of the type are inheriting from some common ancestor. Also it is important that
type variables are resolved at run-time, not at compile-time, like template
typenames.
The solution is templates, of course. Also, we'll use the possibility of
overriding virtual functions with wider return type.
The main problem during the projecting the classes was that all classes are of
some class, so they should be derived from some common ancestor. But, from the
other hand, such a metaclass would be able only to return new instances of type
void, what would disallowed to return more specific types in derived classes.
So, I have decided to create separate root metaclass for each of root classes.
So, that's how it looks (you may also want to look at the source):
template<class T, class I = mcstubmc, class t =
mctraits<I> >
class metaclassdef : public metaclass<I, t> {
public:
virtual T *newinstance() const {
return new T; }
virtual T *newinstance(unsigned
count) const { return new T[count]; }
// for metaobjects
template<class D, class E>
bool
inheritsfrom(const metaclass<D, E> *co) const { return dynamic_cast<
const metaclass<D>*>(this); }
// for objects
template<class D>
bool
objectisa(const D *co) const { return dynamic_cast< const
metaclass<D>*>(this); }
The T parameter is the type that we want to define metaclass of, and I is type
of it's immediate ancestor. Traits and mctraits type is used by
metaclass type to tell what is the actual type of metaclass. Functions newinstance
are clear, I guess. The inheritsfrom function returns true if metaclass
passed as a parameter is an ancestor of this type. And the objectisa can
be called to obtain information if passed object is of class denoted by the
metaclass.
That what I showed you above is only a definition of metaclass. In each class
there should be an instance of appropriate metaclass (this is quite obvious -
instances of metaclasses are classes) defined as static member. Also, it
wouldn't be convenient having to write each time you want to reference a
metaclass all of it's immidiate ancestors. So, we typedef a metaclassdef type
inside a class, and then use it in form T::metacls, where T is the type of
which metaclass we want to obtain. In case you wanted to reference that type
using more standard convention metaclass<T>, there is such template class
defined, that is derived from the member typedef of T class (T::metacls).
Because there is no template typedefs (yet), the class hierarchy is more
complicated. Anyway, mctraits type is used to tell what is the name of that
member typedef.
Also, there is a particular type for classes that have no ancestors. The
difference is only that it is not derived from any class. There is also a type
which inherits from two ancestors. It is quite simple to add metaclasses that
inherit from more than this.
To simplify the defining of needed members there are macros defined. This is one
of it:
This is the one for class that inherits from one class only. Firstly,
metaclass definition is exported for metaclass<T> class. Then, typedef
for use simplified use of metaclass type is defined (T::metacls). The __tp
variable is the instance of metaclass. And the last declaration is function
used to access that instance.
To define the instance there is also a helper macro named metaobjectdef, the
only parameter is the name of class.
The thing that might be interesting to add to this library would be a dynamic
methods (like in Java or C++Builder). They would be called by name. Of course,
it won't look as pretty as in Java, but we have to remember that C++ is a
statically-typed language - but, if someone would really like to, he could
overload the comma operator, and even define constants for strings denoting
methods (who knows, maybe I will try to implement this).
The only thing we have to do is to keep a list of dynamic methods in
metaobjects, and add a method to the classes that want to use it. That method
would simply pass the request to call function with specified name on specified
object. Clear and simple.
And at the end, the problem. The thing is the metaclasses doesn't allow to call
any constructor during dynamic creation of new object. We could use templates,
but, unfortunately, virtual template functions are not allowed. So, you would
have to define class's "virtual constructors" in the class derived from class's
metaclass, and then use the new metaclass in the __metadef typedef.
If you have any questions regarding this library, especially, if you have found
some errors, just email me.