I am currently developing a library in C++ (Mainly because dependent libraries have C++ interfaces). I created a proof-of-concept implementation with C++ interfaces for quick development. The library has to mandatorily provide C interface for it. The C++ interface is good to have but can be modified/removed if it gets in the way of the C interface.
The C++ API right now looks something like this:
typedef struct {
// only basic C data types like int,float.
int a;
float b;
} Object;
typedef struct {
std::vector<Object> objects;
} GroupOfObjects;
typedef struct {
std::vector<GroupOfObjects> groups;
} Result;
typedef struct {
// some basic C data types
int init1;
int init2;
float init3;
// some C++ types which I can possibly replace with something compatible with C
std::string init4;
std::vector<std::string> init5;
} MyClassInitParams;
struct IMyClass {
public:
virtual bool initialize(MyClassInitParams ¶ms) = 0;
virtual bool getResult(Result &result) = 0;
//Other public methods, constructor, virtual destructor
}
// Actual implementation of the above interface
class MyClass : IMyClass {
}
IMyClass *createMyClassInstance();
I have come up with this C interface till now:
extern "C" {
typedef struct MyClass *MyClassHandle;
// A C version of the above MyClassInitParams
typedef struct{
} MyClassInitParams_C;
typedef struct {
Object * objects;
int numObjects;
} GroupOfObjects_C;
// A C version of the above Result structure
typedef struct{
GroupOfObjects_C *groups;
int numGroups;
}Result_C;
MyClassHandle MyClass_Create();
MyClass_Destroy(MyClassHandle handle);
int MyClass_Initialize(MyClassHandle handle, MyClassInitParams_C *params);
int MyClass_GetResult(MyClassHandle handle , Result_C *result);
void MyClass_FreeResult(Result_C *result);
} // end of extern "C"
Implementation of the C interface:
MyClassHandle MyClass_Create()
{
return createMyClassInstance();
}
MyClass_Destroy(MyClassHandle handle)
{
delete handle;
}
int MyClass_Initialize(MyClassHandle handle, MyClassInitParams_C *params)
{
MyClassInitParam params_cpp;
// fill params_cpp using the params structure
return handle->initialize (params_cpp);
}
int MyClass_GetResult(MyClassHandle handle , Result_C *result)
{
Result result_cpp;
bool ret = handle->getResult(result_cpp);
if (!ret)
return 0;
// Fill the C structure using the cpp structure
result->numGroups = result_cpp.groups.size();
result->groups = new GroupOfObjects_C[result->numGroups];
for (int i = 0; i < result->numGroups; i++) {
result->groups[i].numObjects = result_cpp.groups[i].objects.size();
result->groups[i].objects = new Object[result->groups[i].numObjects];
for (int j = 0; j < result->groups[i].numObjects; j++) {
result->groups[i].objects[j] = result_cpp.groups[i].objects[j];
}
}
return 1;
}
void MyClass_FreeResult(Result_C *result) {
// free all the arrays allocated in the above function
}
I have a few questions regarding this:
- The GetResult method has a overhead of copying the objects from C++ vectors to the C arrays. Is there a more elegant and efficient way to handle this?
- I will have to maintain the structures for both C and C++. Should I just use the C versions of the MyClassInitParams and Result structures in C++ interface? This will also help with (1).
- If I use the solution in (2), does it make sense to even have the C++ interface? Or are there any advantages of keeping both the C and C++ interfaces in this case?