在上一节中,我们得到了meta_worker的第一个版本,但是那个版本还不能使用,因为它只有对worker类的描述信息(除regCutPoint外),我们还需要为它添加一些方法去操作它的对象(注意,它的对象不是worker类的对象,而是worker类本身) ,那么我们需要为它添加一些什么方法呢?我们知道,每个C++的类都有构造和析构函数,当我们创建和释放一个对象时,它会自动被调用,而这些都是编译器帮我们完成的(即生成相应的指令代码),另外,当调用一个函数时,我们不用去写参数入栈出栈以及跳转的操作,这些也都由编译器为我们自动完成。类似的,对于元类来说,它也需要提供以下三个操作:构造目标类的对象,析构目标类的对象,以及执行一个目标类的函数调用。(至此,我们应该可以对元类有一个更深刻的理解了,实际上你可以把它理解为一个“类”的解释器。)
为节省篇幅,这里将直接给出上面三个操作的实现,而忽略其声明:
meta_worker::object_data * meta_worker::newObject(const char * name) {
object_data * objData = new object_data;
meta_constructor::arg_wrapper_type argWrapper;
argWrapper.args.that = objData;
argWrapper.args.name = name;
mMetaConstructor.closureEntry(&argWrapper);
return objData;
}
void meta_worker::deleteObject(object_data * objData) {
// 注意,因为worker类没有定义析构函数,所以我们直接释放内存即可,如果定义了析构函数,我们必须先调用析构函数
if (objData)
delete objData;
}
// 下面是第三个操作的实现:
template<typename arg_type>
struct arg_detector: meta_worker::meta_method_args_base {
arg_type methodArgs;
void setArgs(meta_worker::object_data * objData, const arg_type & args) {
that = objData;
methodArgs = args;
}
};
template<>
struct arg_detector<void_type>: meta_worker::meta_method_args_base {
void setArgs(meta_worker::object_data * objData, const void_type & args) {
that = objData;
}
};
template<typename ret_type>
struct ret_detector {
template<typename arg_wrapper_type>
static void setRetVal(ret_type & out_retVal, const arg_wrapper_type & argWrapper) {
out_retVal = argWrapper.retVal;
}
};
template<>
struct ret_detector<void_type> {
template<typename arg_wrapper_type>
static void setRetVal(void_type & out_retVal, const arg_wrapper_type & argWrapper) {
}
};
struct meta_worker_method_invoker {
typedef arg_wrapper<arg_detector<arg_type>, ret_type> arg_wrapper_type;
static void invoke(meta_closure * method, meta_worker::object_data * that, const arg_type & methodArgs, ret_type & out_retVal) {
arg_wrapper_type argWrapper;
argWrapper.args.setArgs(that, methodArgs);
method->closureEntry(&argWrapper);
ret_detector<ret_type>::setRetVal(out_retVal, argWrapper);
}
};
以上三个函数实现均没有考虑切入点的问题,在增加对切入点的支持之前,我们有必要先来讨论一下这三个函数的实现。前两个函数比较简单,也较容易理解,而第三个函数(即执行一个目标类的函数调用)逻辑上并不是很难理解(需结合之前介绍过的模板特化知识),需要讨论的是这个函数的用法。让我们不妨来写一段使用这个函数调用sayHello的例子:
meta_worker metaWorker;
meta_worker::object_data * objData = metaWorker.newObject("Jim");
meta_closure ** metaMethodTable = metaWorker.methodTable();
void_type arg, ret;
meta_worker_method_invoker<void_type, void_type>::invoke(metaMethodTable[1], objData, arg, ret); // 如果不加注释,从这个调用我们完全无法知道它要干什么,而且如果不看 “sayHello”的实现,那个arg和ret的类型为什么是void_type也让人困惑
metaWorker.deleteObject(objData);
从上面的例子不难看出,如果我们直接使用meta_worker_method_invoker去调用一个函数,它的代码可读性非常的差,极其不利于理解(这个例子提醒我们,我们在设计程序或实现一个函数的时候,不能只满足于逻辑的正确性)。那么有没有什么改进的方法呢?有的,方法就是使用闭包。回顾一下闭包的概念,闭包是将一个函数的入口和调用这个函数所需要的参数封装在一起的数据结构,那么我们可以考虑先从meta_worker中返回一个sayHello函数的闭包,然后再去调用它,见下面的代码:
struct say_hello_closure {
meta_worker::meta_say_hello * metaClosure;
meta_worker::meta_say_hello::arg_wrapper_type argWrapper;
void operator ()(meta_worker::object_data * that) {
argWrapper.args.that = that;
metaClosure->closureEntry(&argWrapper);
} // 注意我们利用重载 operator () 来让调用代码更易读
};
bool meta_worker::getMethod(const char * name, void * out_closure, size_t closureSize) {
if ( 0 == strcmp("sayHello", name) && sizeof(say_hello_closure) == closureSize ) {
static_cast<say_hello_closure *>(out_closure)->metaClosure = &mMetaSayHello;
return true;
} else {
// 检查名字并返回其它函数的闭包...
}
}
//下面是用法示例:
meta_worker metaWorker;
meta_worker::object_data * Jim = metaWorker.newObject("Jim");
say_hello_closure sayHello;
metaWorker.getMethod( "sayHello", &sayHello, sizeof(sayHello) );
sayHello(Jim);
metaWorker.deleteObject(Jim);
这个版本是不是看上去就好多了呢?需要指出的是,上面的三个函数都只是针对meta_worker的实现,实际上我们可以只利用runtime_class接口和模板来实现一组通用的操作,有兴趣的朋友可以自己试一试,这里就不再展开讨论了。