【问题标题】:Providing SDK for my C++ Application为我的 C++ 应用程序提供 SDK
【发布时间】:2016-01-12 21:35:21
【问题描述】:

假设我正在用 C++ 创建一个游戏引擎,我只想提供一些头文件而不是提供整个源代码,而这些头文件将需要创建新的游戏实例、提供脚本类、提供游戏对象类以及组件、数学等。

是的,显然我想为我的游戏引擎提供 SDK,但是如何做到这一点,如何只提供一些公共标头并隐藏源文件和仅引擎标头?如何将这些标题链接到源的其余部分?

我在 Linux 平台上使用 Eclipse CDT。

【问题讨论】:

  • 听起来您想要做的是要么生成一个库,您的客户将链接到他们的应用程序,要么您想要生成一个可加载对象的规范(基于您运行的任何平台)并且您提供的二进制文件将加载。也就是说,堆栈溢出的问题有点宽泛。
  • 感谢您的回答,是的,我正在寻找类似的东西,但是如何提供呢?对不起,如果我的问题太宽泛了..

标签: c++ sdk


【解决方案1】:

通常,通过提供共享(动态)库并在标头中提供纯虚拟接口以及一些外部 C 入口点(用于交叉编译器兼容性,如 C++),您将获得最佳(轻松)可实现的二进制兼容性每个编译器都会以不同的方式修改名称)。

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

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

在Linux下,也建议使用编译器的“可见性”标志,默认隐藏所有内容(“-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;
    }
}

这是我实现库接口的原理。建议将分配的对象包装在一个智能 ptr 中,但要对其进行定制,以便它调用 Interface::destroy() 而不是 delete。

还要注意 int32_t 的使用——一般来说,如果你希望接口尽可能地与交叉编译器兼容,你应该使用固定大小的类型(即不是例如 size_t,这也适用于 bool 和 enums,它们都高度依赖于编译器,但对于 int、short、long 等也是如此)。

进一步注意 try/catch 保护的使用,一般来说,如果您希望 API 可能在不同的编译器中使用(或者有时甚至在调试/非调试版本的相同的编译器,但更适用于 Windows;但是,当库与太多不同的版本(例如 GCC 编译器)一起使用时,仍然可能会出现问题。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2011-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多