I keep thinking about how best to implement the interaction between a shared (DLL) library and an application in C++. I settled on the idea of a COM interface. The essence of this idea is that we have purely virtual classes (interfaces) in header files:
class INode
{
public:
virtual ~INode() = default;
virtual ENodeType type() = 0;
virtual void set_name(const char* name_str) noexcept = 0;
[[nodiscard]] virtual const char* name() noexcept = 0;
[[nodiscard]] virtual INode* parent() noexcept = 0;
[[nodiscard]] virtual INode* children() noexcept = 0;
[[nodiscard]] virtual unsigned children_count() noexcept = 0;
virtual EResultCode add_child(INode* node) noexcept = 0;
virtual EResultCode remove_child(INode* node) noexcept = 0;
};
But they are not exported from the library directly, only some functions are exported (in pure C style), which create real objects inside the library and simply return a pointer (of interface type) to this created object:
extern "C"
{
LIB_API INode* create_node();
LIB_API void destroy_node(const INode* node);
}
As a result, the user (of lib) only deals with pointers to objects. This way we don’t need to export classes and at the same time OOP seems to work.
But my goal is to ensure that only information about what architecture the library was compiled on (x86 or x64) would be enough for its reliable use, regardless of the compiler (let’s say the library was compiled by one compiler for x86, but it can be used in application compiled by another compiler for x86)
But several questions arise.
- Is there any guarantee in C++ that standard types (int, float, char*, etc.) will always (in all compilers) be the same size for the same architecture? Is there some way to guarantee this?
- In the case of a COM interface, is it a good idea to return (and pass as an argument to methods) structures? Let’s say I have a structure declared somewhere in the headers (consisting of fields of these standard types), and on the library side the method returns it. Even if I achieve a guarantee that all the fields of the structure will be the same types in any compilers, are there any pitfalls here (like “in compiler A, these structures are packed in one order, and in compiler B – in another + some alignment). I can, of course, return references or pointers to these structures, but this does not change the essence (it is still important how exactly the bytes are located in the structure).
I tried returning structures, and even types from the standard library (such as std::vector), it worked (on same compiler & cpp standard), but the question is – will it always work? I would like to understand what is worth doing and what is not, and what options there are.