gSoap生成的同名不同命名空间SOAP对象的C++类型兼容方案咨询
刚好之前处理过类似的gSoap跨命名空间类型匹配问题,咱们逐个分析你的三个方案,帮你选最优解:
方案1:无需修改生成代码让C++识别为同一类型
别想啦,C++是强类型语言,哪怕两个类型的成员完全一致,只要属于不同命名空间,编译器就会把它们当成完全不同的类型,不可能自动识别成同一个。
硬要用的话只能搞强制类型转换,比如:
soapErrorCode = cloudWebService.action(*reinterpret_cast<myCloudObject*>(&myLocalObject));
但这种操作极度危险——现在两个结构一致不代表以后永远一致,一旦PHP端修改了SOAP对象的结构(比如加个成员、改个类型),这种强制转换直接会导致内存越界、崩溃,而且代码可读性极差,后续维护的人会骂娘的。所以这个方案完全不推荐。
方案2:通过gSoap配置使两者视为同一类型
这才是最优解!gSoap本身就支持处理这种“结构相同但命名空间不同”的类型,具体可以通过几种方式实现:
方式一:生成代码时统一命名空间
如果两个PHP服务器的WSDL文件结构一致,只是命名空间不同,你可以在调用soapcpp2生成C++代码时,指定参数让它们共用同一个命名空间:
- 用
-q<namespace>参数,给两个WSDL生成的代码指定同一个命名空间前缀,比如:
这样生成的soapcpp2 -qMyNamespace local.wsdl soapcpp2 -qMyNamespace cloud.wsdlmyLocalObject和myCloudObject都会属于MyNamespace下的同一个类型(名字可能需要调整,比如都叫MyObject)。 - 或者用
-n<prefix>参数指定生成代码的前缀,让两个WSDL生成的类型名一致。
方式二:用typemap.dat自定义类型映射
如果不想修改命名空间,还可以通过gSoap的类型映射文件typemap.dat来把两个不同命名空间的类型映射到同一个C++类型。比如在typemap.dat里添加:
ns1:SyncObject = ns2:SyncObject | SyncObject
其中ns1是本地WSDL的命名空间,ns2是云端WSDL的命名空间,SyncObject是你要统一的类型名。然后生成代码时带上-t typemap.dat参数:
soapcpp2 -t typemap.dat local.wsdl soapcpp2 -t typemap.dat cloud.wsdl
这样gSoap就会把两个命名空间的SyncObject都生成同一个C++类型,直接就能互相传递了。
这个方案既保证了类型安全,又不用写额外的转换代码,维护成本最低,强烈推荐优先尝试。
方案3:编写转换函数实现类型转换
如果因为某些限制(比如无法修改WSDL、不能调整gSoap生成配置)导致方案2无法实施,那这个就是退而求其次的选择。
你可以写一个手动的转换函数,把myLocalObject的每个成员逐一赋值给myCloudObject:
myCloudObject localToCloud(const myLocalObject& localObj) { myCloudObject cloudObj; // 逐个复制成员 cloudObj.id = localObj.id; cloudObj.name = localObj.name; cloudObj.createTime = localObj.createTime; // ... 所有成员依次处理 return cloudObj; }
调用的时候就改成:
soapErrorCode = cloudWebService.action(localToCloud(myLocalObject));
如果成员特别多,也可以考虑用序列化的方式(比如把对象转成XML字符串,再反序列成目标类型),但手动转换虽然繁琐胜在直观、容易调试。缺点是以后PHP端修改SOAP对象结构时,你必须同步修改转换函数,维护成本比较高。
总结一下优先级:方案2 > 方案3 > 方案1,优先尝试通过gSoap配置统一类型,不行再写转换函数,绝对不要用强制类型转换的hack。
内容的提问来源于stack exchange,提问作者cyanat

