为我的 C++ 应用程序提供 SDK

Providing SDK for my C++ Application

假设我正在用 C++ 创建一个游戏引擎,我只想提供一些 headers 而不是提供整个源代码,而创建新游戏将需要这些 headers例如,提供脚本 class,提供游戏 object class 和组件,数学等..

是的,很明显我想为我的游戏引擎提供 SDK,但该怎么做,如何只提供一些 public headers 并仅隐藏源文件和引擎 headers?如何 link 那些 headers 到源的其余部分?

我在 Linux 平台上使用 Eclipse CDT。

通常,通过提供共享(动态)库并在 headers 中提供纯虚拟接口以及一些外部 C 入口点(对于 cross-compiler 兼容性,因为每个编译器对 C++ 名称的处理方式不同)。

这篇文章可能是一个很好的起点:http://chadaustin.me/cppinterface.html - 它主要针对 Windows,但它也可以应用于 Linux。

实际上我在设计共享库时以此为起点(在 Windows 和 Linux 中工作),但我放弃了自定义运算符 delete 以调用 destroy直接方法(实际上是通过自定义的智能指针)。

在Linux下,还建议使用编译器的"visibility"标志,使所有内容默认隐藏(“-fvisibility=hidden”)并且只标志为__attribute__ ((visibility ("default"))) 需要导出的函数(注意只导出extern "C"入口点,纯虚接口不需要导出)

为了更好的二进制兼容性,您甚至需要避免虚拟方法并实现您自己的虚拟表(与用户可能使用的每个编译器兼容),但纯虚拟接口实际上足够合理地兼容。

对于静态库,您可能会遇到问题,因为您可能需要为用户可能使用的每个编译器(有时甚至是同一编译器的不同版本)提供一个静态库。

例如,界面可能如下所示:

class Interface {
public:
   virtual void destroy() = 0;
protected:
   // prevent to call delete directly
   // - needs to be done in every public interface class
   ~Interface() {}
};

class IGameComponent: public Interface {
public:
    virtual int32_t someMethod() const = 0;
protected:
   ~IGameComponent() {}
};

class IGameEngine: public Interface {
public:
    // call component->destroy() when done with the component
    virtual IGameComponent * createComponent() const = 0;
protected:
   ~IGameComponent() {}
};

extern "C"
__attribute__ ((visibility ("default")))
IGameEngine * createEngine();

实现可以如下所示:

// CRTP to avoid having to implement destroy() in every implementation
template< class INTERFACE_T >
class InterfaceImpl: public INTERFACE_T {
public:
   virtual void destroy() { delete this; }
   virtual ~InterfaceImpl() {}
};

class GameComponentImpl: public InterfaceImpl<IGameComponent> {
public:
    virtual int32_t someMethod() const
    { return 5; }
};

class GameEngineImpl: public InterfaceImpl<IGameEngine> {
public:
    virtual IGameComponent * createComponent() const
    {
        try {
            return new GameComponentImpl;
        } catch (...) {
            // log error
            return NULL;
        }
    }
};

extern "C"
IGameEngine * createEngine()
{
    try {
        return new GameEngineImpl;
    } catch (...) {
        // log error
        return NULL;
    }
}

这就是我实现库接口的原则。建议将分配的 objects 包装在智能指针中,但要进行自定义,以便它调用 Interface::destroy() 而不是 delete.

还要注意 int32_t 的使用 - 一般来说,如果您希望界面尽可能 cross-compiler 兼容,您应该使用固定大小的类型(即不是 size_t,这也适用于布尔和枚举,它们都高度依赖于编译器,但对于 int、short、long 等也是如此)。

进一步注意 try/catch 守卫的使用,如果您预计 API 可能会被不同的编译器使用,通常您不应允许异常通过 API 边界(有时甚至在同一编译器的 debug/non-debug 版本之间,但这更适用于 Windows;但是当库与太多不同版本(例如 GCC 编译器)一起使用时仍然可能存在问题)。 =14=]

这是您想要的视频 -> 在 eclipse 中创建静态库 CDT https://www.youtube.com/watch?v=kw3UD_YCoEk

您也可以创建一个动态库,但首先从静态库开始。