C++語言中的元類編程(五)

在上一節中,我們得到了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) {

    }

};

template<typename arg_type, typename ret_type>
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 {

        // 檢查名字並返回其它函數的閉包...

    }

    return false;

}

//下面是用法示例:

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接口和模板來實現一組通用的操作,有興趣的朋友可以自己試一試,這裏就不再展開討論了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章