1. 程式人生 > >寫gsoap客戶端的懶辦法——巨集函式

寫gsoap客戶端的懶辦法——巨集函式

用了兩天折騰gsoap的用法,寫了個蹩腳的客戶端,因為沒有細緻地去研究wsdl2h和soapcpp2兩個工具的引數,生成的檔案總是看上去不太好記。加上只是臨時任務就直接在centos上編碼了(絕對是壞習慣),沒有提示啥的寫錯太簡單了。為減少人為犯錯的機會,果斷用巨集函式~

巨集函式實現

網上大多關於gsoap的示例中給出的引數都比較簡單,是基本型別的。對於複雜型別的引數就需要用資料類或結構體打包一下,此時生成的介面的型別大概就會這樣:

virtual int GetUpdateTime(
    ns1__GetUpdateTime *ns1__GetUpdateTime_, 
    ns1__GetUpdateTimeResponse &ns1__GetUpdateTimeResponse_){ 
        ......
}

乍看……反正就是要敲好多字的那種,二指彈的很不開心。but很有規律的丫 ~

沒有啥設定的時候函式呼叫啥的寫起來很清爽的樣子:

    calcProxy cal;
    double result = 0;
    if (SOAP_OK==cal.add(1, 2, result))
    {
        cout << result << endl;
    }

加上亂七八糟的超時丫、字元編碼啥的設定,那每個介面呼叫裡都要寫一遍,感覺自己蠢蠢的。
所以乾脆就把初始化和函式呼叫都寫成巨集函式啦~直接丟程式碼咯~

//# init soap
#define
INIT_SOAP_WHICHONE(funcname, timeout, v1) \
hnxnyPortBindingProxy soapProxy; \ soap_set_mode(soapProxy.soap, SOAP_C_UTFSTRING); \ soapProxy.soap->connect_timeout = timeout; \ soapProxy.soap->send_timeout = timeout; \ soapProxy.soap->recv_timeout = timeout; \ ns##v1##__##funcname req; \
ns##v1##__##funcname##Response res; #define INIT_SOAP(funcname, timeout) INIT_SOAP_WHICHONE(funcname, timeout, 1) #define INIT_SOAP2(funcname, timeout) INIT_SOAP_WHICHONE(funcname, timeout, 2) #define INIT_SOAP3(funcname, timeout) INIT_SOAP_WHICHONE(funcname, timeout, 3) #define INIT_SOAP4(funcname, timeout) INIT_SOAP_WHICHONE(funcname, timeout, 4) //# call soap #define SOAP_CALL_WHICHONE(funcname, endPoint, ver) \ { \ int soapRet = soapProxy.funcname(endPoint, NULL, &req, res); \ if (soapRet != SOAP_OK) { return ERR_OVERTIME; } \ } #define SOAP_CALL(funcname, endPoint) SOAP_CALL_WHICHONE(funcname, endPoint, 1) #define SOAP_CALL2(funcname, endPoint) SOAP_CALL_WHICHONE(funcname, endPoint, 2) #define SOAP_CALL3(funcname, endPoint) SOAP_CALL_WHICHONE(funcname, endPoint, 3) #define SOAP_CALL4(funcname, endPoint) SOAP_CALL_WHICHONE(funcname, endPoint, 4)

俺這種小打小鬧級別的巨集函式要看懂只要做到兩點就行:

  1. 預設巨集函式就是字元替換
  2. 程式碼排版要美;

看懂了用起來就十分方便啦,以入參為類的方法示例:

// 初始化
INIT_SOAP(GetRecordings, 1000);

// 輸入
req.arg0 = &measurementPath;
req.arg1 = &startTime;
req.arg2 = &endTime;   

// 介面呼叫      
SOAP_CALL(GetRecordings, url);

// 輸出
results = res.return_;

注意事項

在編寫過程中遇到的幾點要注意的有

1、巨集函式中連線符

很久之前在windows下寫巨集函式,SOAP_CALL_WHICHONE的寫法是:

#define SOAP_CALL_WHICHONE(funcname, endPoint, ver) \
        {   \
            int soapRet = soapProxy.##funcname(endPoint, NULL, &req, res); \
            if (soapRet != SOAP_OK) { return ERR_OVERTIME; } \
        }

編譯報錯指向soapProxy.##funcname所在行: does not give a valid preprocessing token。
解決方案是去掉funcname前的連線符##,這個地方出錯主要是因為soapProxy.的緣故,在這種情況下連線符的解析出錯。
參考解決方案:https://bbs.csdn.net/topics/300111770

2、soapcpp2引數

因為使用了soapProxy.soap來設定一些引數,因此在生成的時候需要使用引數-j而不是-i

引數 作用
-1 Soap1.1繫結
-2 SOAP1.2繫結
-C 只生成客戶端程式碼
-S 只生成伺服器端程式碼
-T 生成自動測試程式碼
-L 不生成 soapClientLib/soapServerLib
-a 用 SOAPAction 和WS-Addressing呼叫伺服器端方法
-A 用 SOAPAction 呼叫伺服器端方法
-b 採用char[N]這樣的方式來表示string
-c 生成的是C程式碼,不是C++程式碼
-d < path > 將程式碼生成在 < path >下
-e 生成 SOAP RPC 樣式的繫結
-f N File split of N XML serializer implementations per file
-h 顯示一個簡要的用法資訊
-i 生成的服務代理類和物件從struct soap繼承而來
-j 生成的服務代理類和物件包含struct soap而來(C程式碼的唯一選擇)
-I < path > 包含其他檔案時使用,指明 < path > (多個的話,用`:’分割),相當於#import ,該路徑一般是gSOAP目錄下的import目錄,該目錄下有一堆檔案供soapcpp2生成程式碼時使用。
-n 用於生成支援多個客戶端和伺服器端(具體內容參考gSOAP文件)
-p < name > 生成的檔案字首採用< name > ,而不是預設的 “soap”
-q < name > C++程式碼中,所有宣告的名稱空間
-s 生成的程式碼在反序列化時,嚴格檢查XML的有效性
-t 生成的程式碼在傳送訊息時,採用xsi:type方式
-u 在 WSDL/schema 輸出檔案中不產生XML註釋
-v 顯示版本資訊
-w 不生成 WSDL 和 schema 檔案
-x 不生成 XML 形式的傳輸訊息檔案
-y 在XML 形式的傳輸訊息檔案中,包含 C/C++型別資訊