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

Providing SDK for my C++ Application

本文关键字:SDK 应用程序 我的 C++      更新时间:2023-10-16

假设我正在C++中创建一个游戏引擎,我只想提供一些标题,而不是提供整个源代码,并且需要这些标题来创建新的游戏实例、提供Script类、提供游戏对象类和组件、数学等

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

我在Linux平台上使用Eclipse CDT。

通常,通过提供共享(动态)库并在头文件中提供纯虚拟接口,以及一些外部C入口点(为了实现跨编译器兼容性,因为每个编译器对C++名称的处理方式不同),可以获得最佳(容易)实现的二进制兼容性。

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

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

在Linux下,还建议使用编译器的"visibility"标志,默认情况下将所有内容都隐藏("-fvisibility=hidden"),并且只将需要导出的函数标记为__attribute__ ((visibility ("default")))(注意,您只需要导出外部"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和enum,它们都高度依赖于编译器,但int、short、long等也是如此)。

进一步注意尝试/捕捉防护装置的使用,通常,如果您希望API可以从不同的编译器使用,则不应允许异常通过API边界(有时甚至在同一编译器的调试/非调试版本之间使用,但这更适用于Windows;但是,当库与太不同的版本(如GCC编译器)一起使用时,仍然可能会出现问题)。

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

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