Providing SDK for my C ++ application

Let's say that I am building a C ++ game engine and I want to provide only some headers instead of providing all the source code, and these headers will be needed to create a new game instance, provide the Script class, provide the game with an object class and components, math, etc. etc.

Yes, obviously, I want to provide an SDK for my game engine, but how do I do that, how do I only provide some public headers and hide the source files and only the engine headers? How do I relate these headers to the rest of the source?

I am using Eclipse CDT on Linux platform.

0


source to share


2 answers


In general, you get the best (easily) achievable binary compatibility by providing a shared (dynamic) library and providing a clean clean interface in headers, with some external C entry points (for compatibility with multiple compilers, how C ++ names are mangled by each compiler differently).

This article might be a good starting point: http://chadaustin.me/cppinterface.html - it focuses mainly on Windows, however it can apply to Linux as well.

I actually used this as a starting point when developing a shared library (working on both Windows and Linux), but I removed the custom delete operator in favor of calling the destroy method directly (actually using a configured smart pointer).

On Linux it is also recommended to use the compiler visibility flag, make everything hidden by default ("-fvisibility = hidden") and only the flag as __attribute__ ((visibility ("default")))

functions that need to be exported (note that you only need to export the "C" external entry point and pure virtual interfaces do not need to be exported).

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

With static libraries, you may have problems because then you may need to provide a static library for each compiler (and sometimes for different versions of the same compiler) that the user may be using.

As an example, the interface might look like this:



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 might look like this:

// 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 basically how I implemented the library interface. It is advisable to wrap the selected objects inside a smart ptr, but configure so that it calls the :: destroy () interface 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, but also when applied to bool and enums, which are all highly compiler dependent, but even for int, short, long, etc.).

Note the use of try / catch guards, in general you should not allow exceptions to traverse the API boundary if you expect the API to be used from another compiler (or sometimes even between debug / debug versions of the same compiler, but this is more Windows-specific, but problems can arise when the library is used with too different versions, for example, the GCC compiler).

+2


source


This is a video of what you want -> creating a static library in eclipse CDT https://www.youtube.com/watch?v=kw3UD_YCoEk



You can also create a dynamic library, but start with a static one first.

0


source







All Articles