用了两天折腾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)
俺这种小打小闹级别的宏函数要看懂只要做到两点就行:
- 预设宏函数就是字符替换;
- 代码排版要美;
看懂了用起来就十分方便啦,以入参为类的方法示例:
// 初始化
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++类型信息 |