An example will clarify:
class Object { ... }; // Base object
class List: public Object { list <Object*> objs_; };
The whole library is based on Object. So containers (like List) are Objects themselves. Clear enough. Now: the List object should have a method for appending an object, say "append", which can be used from the language (i.e. a dynamic method for executing a method giving a name).
list->send("append", someObject);
The problem with it is that it's sooo slow to execute, and absolutely doesn't fit a library. If you're using a C++ library, you expect to append an object like this:
list->append(someObject);
Here's the two level: the same code (append), must be present both registered as "append" message AND append() method. To implement this effectively in C++ is quite a pain. Why? Let's see.
Is it clear that you'll need to have a append() method in List class, as usual C++:
class List {
...
void append(Object *o) { objs_.append(o); }
};But also, we need to have a "append" message registered somewhere; messages are dispatched by methods objects, that is an Object that's like:
class Append_message: public Object {
Object *execute(Object *caller, list<Object> parameters) {
List *l = dynamic_cast<List<(caller);
// Error checking
l->append(parameters.first);
return caller;
}
};This because, being the whole library composed by Objects, code must be an Object too. This class will be instantiated and the instance will be called "append" and registered in the List objects and finally used by the language.
Now: is it evident how much this code is "just a mapping". But there are some questions:
First, where's Append_message defined? Since it's an append()-related thing, it would be nice to be defined together with append().
Second, is there a way to define every XYZ_message in an automated way? Are they "just mapping function objects"?
The second question is, in some ways, related to the first: what if methods and message should behave differently? It would be nice to have in the same place the specifications of these behaviors.
So I tired to answer the first question with functors - this would solve the second question too: "let the implementor decide if they're just mapping or not".
This is what I aimed to do (pseudo-C++, but gives the idea).
class Append: public Object {
void operator()(Object *o) {
objs_->append(o);
}
Object *exec(Object *caller, list<Object*> params) {
List l = /* convert the caller */;
l->append(params.first);
return caller;
}
};
cass List: public Object {
list<Object *> objs_;
public:
Append append("append");
};
list->append(o);
list->send("append", o);Got the idea? Define a function and the mapping with the language in the same place, possibly in a functor (it can be reused in different objects).
Probably you noticed it immediately: objs_ isn't defined in Append classes! The code can't reach it, since it's private to the Append class. A solution would be to make the Append friend, or just to have a public obj_.
Friends aren't always a good thing and public members are against information hiding - they're really bad.
Multiple inheritance won't help because a base class can't refer to a member of a derived class - and of course naming collisions may be disturbing.
I would like to add something to the answer to the second question: is it possible to build a default mapping pattern? Something like "method-to-functor".
The problem of having such a technique is correlated in having a types-mapping system: when you define a method for your C++ class, it's in the form
RetType Name(ParameterList)
And you need to map it to:
Object *exec(Object *caller, list<Object*> params)
So one need a system to map:
1. "Object *caller" to an object of your type (e.g. List *). This is straightforward, as it's always a dynamic_cast<Target *>(caller).
2. Each parameter to an object of the relative type. For example, if a function take an int, it should be converted to an Integer object. Of course, Objects don't need any conversion.
3. The return value, same as above.
The problem is that constness, references, pointers and such types have a semantic, and the mapping should consider this semantic.
In addition, there's no guarantee on how many parameters a method has.
Well, I never tried C++0x (err, of course :D I couldn't try it... But >=gcc-4.3 does implement some features of the language) so I still didn't tried to do it in code, but I think that templates will be boosted enough to help me in this case.
In particular, variadic templates may be useful.
I hope to be able to do some template metaprogramming that does the job. I'm still learning it, but I've some ideas that may be useful if combined with variadic templates... In particular, templates manifest kind of "duck typing" features that can be essential in this approach.
My idea is to be able to do what I meant with something like:
typedef BaseList<Append, And, Every, Other, Method, Here> List;
And just use the template system to do what I needed. Maybe it isn't possible. I'll write more later, whenever I find something useful.
Btw, how to resolve the naming issue? Every method must have a name!
typedef BaseList<Append, "append", And, "and", ...> List?
(I think string literals aren't usable in templates. So this method is excluded).
or
class Append { string name; /* = "append" */ ... };
?
Stay --sync