为我的C ++应用程序提供SDK [英] Providing SDK for my C++ Application

查看:104
本文介绍了为我的C ++应用程序提供SDK的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



显然我想为我的游戏引擎提供SDK,但如何做,如何只提供一些公共标头和隐藏源文件和引擎只有头?



我在Linux平台上使用Eclipse CDT。



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

我实际上使用它作为一个起点设计一个共享库(在Windows和Linux中工作),但我删除了自定义运算符删除赞成调用destroy方法直接(实际上是通过自定义的智能指针)。



在Linux下,建议使用编译器的visibility标志, -fvisibility = hidden)并且只标记为 __ attribute__((visibility(default))))需要导出的函数externC入口点和纯虚拟接口不需要导出)。



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



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



例如,例如:

  class Interface {
public:
virtual void destroy()
protected:
//防止直接调用删除
// - 需要在每个公共接口类中完成
〜Interface(){}
};

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

class IGameEngine:public Interface {
public:
//使用组件完成时调用component-> destroy()
virtual IGameComponent * createComponent = 0;
protected:
〜IGameComponent(){}
};

externC
__attribute__((visibility(default)))
IGameEngine * createEngine

实现方式如下:

  // CRTP,以避免在每个实现中实现destroy()
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(...){
//日志错误
return NULL;
}
}
};

externC
IGameEngine * createEngine()
{
try {
return new GameEngineImpl;
} catch(...){
//日志错误
return NULL;
}
}

这是在我如何实现接口的原理图书馆。建议将分配的对象包装在智能ptr中,但是需要定制,以便调用Interface :: destroy()而不是delete。



还要注意使用int32_t - 一般来说,如果你想让接口尽可能的交叉编译兼容,你应该使用固定大小的类型(即不是例如size_t,也适用于bool和enums,这些都是高度编译依赖的,所以对于int,short,long等)。



进一步注意使用try / catch守卫,一般来说,你不应该允许异常通过API边界,如果你期望API可能从不同的编译器(或有时甚至在同一个编译器的调试/非调试版本之间使用,但适用于更多的Windows;但是仍然可能会出现问题,当库将使用太不同的版本例如GCC编译器)。


Let's say that I'm creating a game engine in C++ and I want to provide only some headers instead of providing whole source code, and those headers will be needed to create new game instance, provide Script class, provide game object class and components, math, etc..

Yeah obviously I want to provide SDK for my game engine but how to do it, how to provide only some public headers and hide source files and engine only headers? How to link those headers to the rest of the source?

I'm using Eclipse CDT on Linux platform.

解决方案

In general, youd'd get the best (easily) achievable binary compatibility by providing a shared (dynamic) library and provide pure virtual interface in the headers, with some extern C entry points (for cross-compiler compatibility, as C++ names are mangled by each compiler differently).

A good starting point could be this article: http://chadaustin.me/cppinterface.html - it is mainly targeted to Windows, however it can be applied to Linux as well.

I've actually used that as a starting point when designing a shared library (working in both Windows and Linux), but I dropped the custom operator delete in favor of calling the destroy method directly (actually by a customized smart pointer).

Under Linux, it is also advisable to use the "visibility" flag of the compiler, make everything hidden by default ("-fvisibility=hidden") and only flag as __attribute__ ((visibility ("default"))) the functions which need to be exported (note that you only need to export the extern "C" entry point and the pure virtual interfaces do not need to be exported).

For even better binary compatibility you would need to avoid even virtual methods and implement your own virtual tables (compatible with every compiler the user might use), but pure virtual interfaces are actually compatible reasonably enough.

With static libraries you might have issues, because you might then need to provide a static library for every compiler (and sometimes even different versions of the same compiler) the user might use.

As an example, the interface might look like:

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();

The implementation can then look like:

// 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;
    }
}

This is in the principle how I implemented the interface of the library. It is advisable to wrap the allocated objects inside a smart ptr, but customized so that it calls Interface::destroy() instead of delete.

Also note the use of int32_t - in general, if you want the interface to be as cross-compiler compatible as possible, you should use fixed size types (i.e. not for example size_t, and that also applies to bool and enums, which all are highly compiler dependent, but even so for int, short, long etc.).

Further note the use of try/catch guards, in general you should not allow exceptions to pass the API boundary if you expect the API might be used from a different compiler (or sometimes even between debug/non-debug versions of the same compiler, but that applies more to Windows; however there still could be issues when the library will be used with too different version of e.g. the GCC compiler).

这篇关于为我的C ++应用程序提供SDK的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆