• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            qiezi的學習園地

            AS/C/C++/D/Java/JS/Python/Ruby

              C++博客 :: 首頁 :: 新隨筆 ::  ::  :: 管理 ::

            一、問題。

            這段時間考慮實現一個純C++的分布式服務包裝庫,簡要描述如下:

            有如下類和函數:

            struct?Test
            {
            ????
            void?test1?(/*in*/?int?v1,?/*in*/?int*?v2);
            ????
            int?test2?(/*in*/?int&?v1,?/*out*/?int*?v2);
            };

            int?test_func?(/*in*/?int*?v1,?/*inout*/?string*?v2);


            想把它們作為服務發布出去,以SOAP或其它方式。發布為一個TestService,并讓它攜帶多一些信息:

            struct?TestService
            {
            ????
            void?test1?(in<int>?v1,?in<int>?v2);
            ????
            int?test2?(in<int>?v1,?out<int>?v2);
            ????
            int?test_func?(in<int>?v1,?inout<string>?v2);
            };


            C++有許多工具、庫來做到這點,但是,都需要生成一堆代碼,很是不爽。

            其它語言,比如python, java, c#等,都可以通過自省機制,拋開IDL在語言內實現。

            C++并非不能做這個,它只是缺少足夠的類型信息。比如上面的例子,如果要發布為服務,那么至少應該把它的參數、返回值搞得明確些,否則要么會造成不必要的參數傳遞,要么會產生錯誤(把OUT參數取值可不是安全的)。

            比如上面出現的int, int&, int*,在作為in參數時,我們是想傳遞它的值,類型為int。而int*和string*作為out參數時,我們想讓它傳遞指針或引用,當調用返回時,我們給它賦值。

            C++語言的類型極為豐富,卻沒有描述一個參數到底是in還是out。java也沒有,但它可以正常序列化一個null值,在C++中,這可能存在一些麻煩。

            再考慮一下char*類型,假如它是in參數,那么它是要傳遞一個字符還是一個字符串?C++語言沒有對它進行描述。

            所以要實現一個分布式服務包裝(或代理)庫,必須讓發布者提供這些信息。

            我們知道,要查詢一個遠程服務,必須查詢相應主機端口,獲取服務信息。最簡單的服務信息包括:服務列表,每個服務中的方法列表,方法的類型(包括參數和返回值類型,in/out信息等)。

            實際上,我們是要為C++增加一些簡單的自省能力。上面那個服務發布接口,實際上離這個要求還有很遠,再來看一下:

            struct?TestService
            {
            ????
            void?test1?(in<int>?v1,?in<int>?v2);
            ????
            int?test2?(in<int>?v1,?out<int>?v2);
            ????
            int?test_func?(in<int>?v1,?inout<string>?v2);
            };


            可以想見,它是沒有一點自省能力的,我們如何向它查詢,它的名字?它的方法列表?方法的類型?它如何與Test類的成員函數以及test_func函數關聯?

            二、方向。

            要讓上面那個服務具有自省能力,要做的擴充其實并不多??紤]下面的代碼:

            struct?TestService?:?public?Service
            {
            ????TestService?();
            ????Method?
            <void(in<int>,?in<int>)>?test1;
            ????Method?
            <int(in<int>,?out<int>)>?test2;
            ????Method?
            <int(in<int>,?inout<string>)?test_func;
            };


            這幾個Method可以用自己寫的委托類來做。

            1、假如我們在TestService的構造函數里給它分配一個“TestService”名字,并且Service類實現了查詢名字的接口,那么它就知道它自己的名字了。

            2、假如在TestService的構造函數里為各個Method分配名字,并且注冊到TestService,那么它就能夠查詢方法列表。

            3、方法的類型?通過模板方式,把各個參數類型收集起來,給個字符串名稱就可以了。

            使用宏來實現,大概可以寫成這樣:

            BEGIN_SERVICE?(TestService)
            ????METHOD?(
            void(in<int>,?in<int>),?test1,?&Test::test1)
            ????METHOD?(
            int(in<int>,?out<int>),?test2,?&Test::test2)
            ????METHOD?(
            int<in<int>,?inout<string>),?test_func,?test_func)
            END_SERVICE?()


            通過上面這幾個宏,我們能夠生成TestService聲明。

            不過,有幾個問題,羅列如下,并一一解決它:

            1、如何把函數指針傳給它?如何把方法名稱傳給它?
            這個只是C++語言為我們增加了一些麻煩,我們無法在定義成員的地方調用它的構造函數,不過這并不會造成多大障礙。
            上面的METHOD宏如果只是生成類的聲明,那么函數指針可以省略。我把它加上的原因是,它可以被我用Ctrl+C, Ctrl+V這種世界上最先進的技術原樣拷貝下來,并且通過簡單修改的方法實現這種世界上最先進的重用。

            上面的代碼經過修改,結果就成這樣:

            BEGIN_SERVICE?(TestService)
            ????METHOD?(
            void(in<int>,?in<int>),?test1,?&Test::test1)
            ????METHOD?(
            int(in<int>,?out<int>),?test2,?&Test::test2)
            ????METHOD?(
            int<in<int>,?inout<string>),?test_func,?test_func)

            ????BEGIN_DEFINE?(TestService)
            ????????METHOD_DEFINE?(
            void(in<int>,?in<int>),?test1,?&Test::test1)
            ????????METHOD_DEFINE(
            int(in<int>,?out<int>),?test2,?&Test::test2)
            ????????METHOD_DEFINE(
            int(in<int>,?inout<string>),?test_func,?test_func)
            ????END_DEFINE?()

            END_SERVICE?()


            看上去對應得非常整齊,修改起來也比較簡單。上面那部分被擴充為如下代碼:

            struct?TestService?:?public?Service
            {
            ????Method?
            <void(in<int>,?in<int>)>?test1;
            ????Method?
            <int(in<int>,?out<int>)>?test2;
            ????Method?
            <int(in<int>,?inout<string>)?test_func;
            ????TestService?()
            ????:?Service?(
            "TestService")
            ????{
            ????????test1.setName?(
            "test1");
            ????????test1.setMethod?(
            &Test::test1);
            ????????
            this->registerMethod?(&test1);
            ????????test2.setName?(
            "test2");
            ????????test2.setMethod?(
            &Test::test2);
            ????????
            this->registerMethod?(&test2);
            ????????test_func.setName?(
            "test_func");
            ????????test_func.setMethod?(test_func);
            ????????
            this->registerMethod?(&test3);
            ????}
            };


            基本上需要的東西都在這里了。

            2、客戶端的問題。

            上面這種映射,直接拿到客戶端會有問題,Test類和test_func函數我們并不打算交給客戶端,所以使用函數指針會出現鏈接錯誤。

            實際上客戶端不需要這個,我們想辦法把它拿掉就行了。客戶端實際需要生成的代碼如下:

            struct?TestService?:?public?Service
            {
            ????Method?
            <void(in<int>,?in<int>)>?test1;
            ????Method?
            <int(in<int>,?out<int>)>?test2;
            ????Method?
            <int(in<int>,?inout<string>)?test_func;
            ????TestService?()
            ????:?Service?(
            "TestService")
            ????{
            ????????test1.setName?(
            "test1");
            ????????
            this->registerMethod?(&test1);
            ????????test2.setName?(
            "test2");
            ????????
            this->registerMethod?(&test2);
            ????????test_func.setName?(
            "test_func");
            ????????
            this->registerMethod?(&test3);
            ????}
            };


            還是上面提到的,C++給我們帶來的麻煩。這次需要另一組宏來完成它:

            BEGIN_SERVICE_D?(TestService)
            ????METHOD_D?(
            void(in<int>,?in<int>),?test1)
            ????METHOD_D?(
            int(in<int>,?out<int>),?test2)
            ????METHOD_D?(
            int(in<int>,?inout<string>),?test_func)

            ????BEGIN_DEFINE_D?(TestService)
            ????????METHOD_DEFINE_D?(
            void(in<int>,?in<int>),?test1)
            ????????METHOD_DEFINE_D(
            int(in<int>,?out<int>),?test2)
            ????????METHOD_DEFINE_D(
            int(in<int>,?inout<string>),?test_func)
            ????END_DEFINE_D?()

            END_SERVICE_D?()


            METHOD*和METHOD_DEFINE*宏的參數都有一些多余的信息,沒有去掉是因為放在一起容易看到寫錯的地方。(這個技巧來源于前幾天看的一篇BLOG,很報歉沒有記下地址)

            3、使用的問題。

            如何才能比較方便地使用?我考慮了下面這種方式:

            template?<class?T>
            struct?IProxy;

            template?
            <class?T>
            struct?SOAPProxy;

            SOAPProxy?
            <TestService>?service;
            service.connect?(
            5000,?"localhost");
            int?a=0;
            int?*n?=?&a;
            service.test1?(
            3,?n);
            service.test1?(
            3,?*n);
            service.test2?(
            3,?n);
            service.test2?(
            3,?*n);
            service.test2?(
            3,?NONE);
            //


            Method::operator ()的各個參數都將可以接受相容的類型,像上面一樣,因為在TestService中我們已經定義了它要傳輸的值的類型。

            a.NONE是什么?其實是為異步調用考慮的。假如指定某個OUT參數為NONE,則這個參數的值并不真正的OUT,而是保存在Method中。實際上Method中保存每個參數的值。

            b.Method與Service如何發生關系?
            從TestService的定義中我們知道,Method向Service注冊自己以實現自省,但它同時也會保存Service的指向。
            我們的Proxy實際上是一個繼承模板,上面并沒有把它指出來。它的定義是:

            template?<class?T>
            class?XProxy?:?public?T
            {
            ????
            //
            };


            所以我們的TestService其實也是模板類,它將使用XProxy中定義的序列化類。XProxy將實現Service基類中序列化虛函數以及調用虛函數。

            當一個Method調用時,它會調用Service的序列化,由于被重寫了,所以調用的是XProxy中的序列化方法。這個方法會把這個Method的各in/inout參數序列化,然后執行遠程調用,再把調用結果反序列化給inout/out參數。

            4、其它想法。

            在考慮上面的定義方式時,我也考慮了其它方式,主要是返回值處理的方法,簡述如下。

            前面我們假設了一段將被開放為遠程服務的代碼:

            struct?Test
            {
            ????
            void?test1?(/*in*/?int?v1,?/*in*/?int*?v2);
            ????
            int?test2?(/*in*/?int&?v1,?/*out*/?int*?v2);
            };

            int?test_func?(/*in*/?int*?v1,?/*inout*/?string*?v2);


            在前面的做法中,我們的服務描述是放在那一組宏里面,好處是不用改這段代碼,壞處就是代碼定義的地方和描述不在一起,協調可能會有一些不便。

            我也考慮了另一種做法:

            struct?Test
            {
            ????idl?
            <void(in<int>,?in<int>)>?test1?(int?v1,?int*?v2);
            ????idl?
            <int(in<int>,?out<int>)>?test2?(int&?v1,?int*?v2);
            };

            idl?
            <int(in<int>,?inout<string>)?test_func?int*?v1,?string*?v2);


            對于實現代碼,只需要修改返回值為void的函數,把return;修改為return VOID;,并且為沒有寫此語句的分支加上此句。

            VOID是一個特殊類型的靜態變量,專為void返回值的函數設定。

            這種做法修改了原有的代碼,不過在定義服務時可以節省一些工作:

            BEGIN_SERVICE?(TestService)
            ????METHOD?(test1,?
            &Test::test1)
            ????METHOD?(test2,?
            &Test::test2)
            ????METHOD?(test_func,?test_func)

            ????BEGIN_DEFINE?(TestService)
            ????????METHOD_DEFINE?(test1,?
            &Test::test1)
            ????????METHOD_DEFINE?(test2,?
            &Test::test2)
            ????????METHOD_DEFINE?(test_func,?test_func)
            ????END_DEFINE?()

            END_SERVICE?()


            它所需要的函數類型,將由函數指針推導。

            在G++編譯器下,可以使用typeof來獲得函數指針的類型而不需要真得獲得函數指針值,不過目前僅僅在G++下可用。(順便說一下,typeof已經列入c++0x)

            最終我放棄了這個想法,畢竟它要修改現有的代碼,某些情況下這是不可能的,而且typeof目前也不能跨編譯器。

            三、實現。

            老實說我現在還沒有一份完整的或半完整的實現,大部分想法還在頭腦中,測試代碼倒是寫了不少,主要是用來測試上述想法能否實現,我想大部分情況都已經測試了,只需要有時間來把它實現出來。

            這是我近期要做的事之一,爭取月內把它做完罷。

            亚洲综合久久夜AV | 99久久国产综合精品五月天喷水 | 久久99精品久久久大学生| 伊人久久大香线蕉综合影院首页| 亚洲色婷婷综合久久| 一本色道久久88加勒比—综合| 精品久久久久久无码免费| 久久99精品国产麻豆宅宅| 91性高湖久久久久| 久久久久亚洲AV无码专区首JN| 久久精品一区二区三区不卡| 亚洲午夜福利精品久久| 青青草原1769久久免费播放| 一级A毛片免费观看久久精品| 久久精品国产亚洲av水果派| 久久久久亚洲av成人无码电影 | 伊人久久久AV老熟妇色| 国产精品午夜久久| 无码国内精品久久人妻| 一本色综合久久| 久久99精品国产麻豆不卡| 2021久久国自产拍精品| 久久久久久久波多野结衣高潮 | 久久久久av无码免费网| 国产精品久久久久久久久免费| 亚洲精品NV久久久久久久久久 | 伊人久久大香线蕉精品不卡 | 久久精品国产99国产精品亚洲| 久久国产精品免费一区| 久久99热狠狠色精品一区| 浪潮AV色综合久久天堂| 狠狠色婷婷久久一区二区| 午夜精品久久久久久| 三级韩国一区久久二区综合| 国产精品美女久久久免费| 日本道色综合久久影院| 久久综合中文字幕| 久久www免费人成看国产片| 久久综合狠狠综合久久激情 | 9999国产精品欧美久久久久久 | 亚洲欧美国产精品专区久久 |