移动跨平台框架开发之一:ios重用c++库

最近我们在开发一款游戏,包含四块:c++服务器,ios客户端,android客户端以及c++的客户端。C++客户端用于集成测试以及压力测试。我们希望达到最大限度的重用。C++是自然的选择。我们需要把c++的源代码以库的形式重用在ios和andriod上。这样网络通信和model部分只要维护一套c++代码,ios和android只要写UI和线程。后续将分篇讲述开发中碰到的问题和解决方案。

今天讲述ios重用c++库。

这里有两个难点:

  1. 我们用到了大量的第三方库,主要有:

l   zmq                    socket通讯

l   protobuf            协议

l   cryptopp           加密鉴权

这些库的源代码有的比较旧;有的有好几个来源,不知该选择哪个;脚本都需要改动才能在你的平台下运行,但文档并没有提及。

  1. c++与obj-c的混合编码

一般人可能觉得这很简单,只要把整个工程的.m和.cpp文件全部改为.mm就可以了。如果你写一个简单的helloword程序,这样确实没问题。前面我们说过,我们要以库的形式重用c++,就有一点tricky了。

言归正传,以下阐述过程中遇到的问题和解决方案。

  1. 编译protobuf

下载c++的代码库,最新版本是2.5.0。按照

http://www.giraffe-games.com/using-protobuf-protocol-buffers-on-iphone-ios/

step by step就可以了。其中有一步执行脚本:

https://github.com/dinote/protobuf-mobile-build/blob/master/ios-config.sh

这个脚本比较新。只要把其中的

export SDKVER="6.0"

替换成你的sdk版本,比如我替换成6.1。

提醒一点,由于以上编译出来的是适用于移动终端的lite版本,因此请在.proto文件的头部加上这一行,否则链接失败。

option optimize_for = LITE_RUNTIME;

lite版本的体量小很多,但它的代价是不能在程序中使用DebugString()等方便调试的函数了。

不要用for objc的代码库。它编译后生成包含c++和objc wrapper的单一库。它的最新版本是2.2.0。由于这个编译出来的库是现成的(我同事编译的),我曾经尝试用2.2.0的protoc来编译.proto文件,生成.h和.cc文件,并在objc程序中以c++的方式调用,结果链接失败。

  1. 编译cryptopp

这个库资料不完整并且不正确。首先容易弄错的是下载了不正确的库。请下载

https://github.com/yep/CryptoPP-for-iOS

这里你需要改动的是文件:external / scripts / build-cryptopp.sh

       export DEV_ROOT="/Applications/Xcode.app/Contents/Developer/Platforms/${PLATFORM}.platform/Developer"

       export TOOL_ROOT="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain"            //加这一行

       export SDK_ROOT="${DEV_ROOT}/SDKs/${PLATFORM}${SDK_VERSION}.sdk"

       BUILD_PATH="${WORK_PATH}/objs/${PLATFORM}${SDK_VERSION}-${ARCH}.sdk"

       export CC="${DEV_ROOT}/usr/bin/gcc -arch ${ARCH}"

       export LD=${DEV_ROOT}/usr/bin/ld

        if [ "${ARCH}" == "i386" ]; then

              export CXX=${DEV_ROOT}/usr/bin/g++

        else

              export CXX=${TOOL_ROOT}/usr/bin/clang          //改这一行

不要下载在google搜索中排名靠前的

https://github.com/3ign0n/CryptoPP-for-iOS

前者比后者略新,错误少点,改起来就没那么费劲了。我因为下载了这个库,浪费了大量时间。

编译出来的库很大,有100+M,没有关系。链接程序只会选择用到的部分,生成的.app只会增加很小的体量。

  1. 编译zeromq

我同事以前编译的,生成两个库:libzmq.a和libzmqobjc.a。只需要用前者就行。

  1. c++和objc混合编程

这里的关键是要保持代码的隔离。工程中混合的需要改名为.mm的文件越少越好。

Objc调用c++相对简单,只要在Objc的类中包含c++的成员对象,并且调用该对象的成员函数就可以了。例如我的一个NSOperation子类中只包含这几行代码

- (id)init

{

    self = [super init];

    msgDispatcher = new MsgDispatcher();

    return self;

}

 

- (void)main

{

    @autoreleasepool

    {

        msgDispatcher->dispatch();

    }

}

其中msgDispatcher类在c++编译成的库中。

c++调用Objc就很有讲究了,因为这部分代码是不能包含Objc的头文件和成员变量的。为了防止c++对Objc的依赖,按照正常的逻辑,声明一个c++虚类,这个类位于需要重用的c++编译成的库中。由Objc继承并实现它的虚函数。遗憾的是,objc与c++之间不能互相继承,双方向都不行。接下来你会想到用什么?对,cast。以下是代码示例。

MsgHandlerImpl.h

class MsgHandlerImpl

{

public:

    MsgHandlerImpl ( void );

    ~MsgHandlerImpl( void );

   

    void onCheckin ();

   

private:

    void * viewControllerAdapter;

};

MsgHandlerImpl::MsgHandlerImpl()

{

    viewControllerAdapter = (void *)CFBridgingRetain([[ViewControllerAdapter alloc] init]);

}

 

MsgHandlerImpl.cpp

MsgHandlerImpl::~MsgHandlerImpl()

{

   

}

 

void MsgHandlerImpl::onCheckin()

{

    [(ViewControllerAdapter *)CFBridgingRelease(viewControllerAdapter) onCheckin];

}

这里关键中的关键就是CFBridgingRetain和CFBridgingRelease。作用类似于cast。

千万不要用(__bridge void *)作强制类型转换。

viewControllerAdapter = (__bridge void *) [[ViewControllerAdapter alloc] init];

  1. 其它问题

以下都是我走了很多弯路之后得来的。

5.1.     文件类型

问题:

编译时提示大量系统头文件内部的问题

解决方案:

多半是你没有把.m文件或者.cpp文件改为.mm文件。虽然你的.m文件没有直接包含c++代码,但只要是include c++的头文件,或者import的objc头文件中间接include c++的头文件,都需要改后缀名为.mm文件,或者在file inspector中改file type为object c++ source。后者更为方便。

5.2.     std链接

问题:

编译时提示大量std::string的问题

解决方案:

In main project -> Build Settings scroll and find out the options, C++ Language Dialect and C++ Standard Library. Select options "Compiler Default" for both of them.

5.3.     Zmq包含的头文件找不到

问题:

‘algorithm’ it not found

解决方案:

header search path中添加/usr/include/c++/4.2.1

原文地址:https://www.cnblogs.com/mobileinternet/p/3185124.html