Reputation: 51
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:
Upvotes: 5
Views: 464
Reputation: 907
I'd suggest to return Result* from C MyClass_GetResult method, like int MyClass_GetResult(MyClassHandle handle, Result_C **result)
or Result_C* MyClass_GetResult(MyClassHandle handle)
. Then, add accessors for groups and objects.
It's up to you to decide, but I'd rather to use one or the other, but not both.
First of all, I'd suggest to decide what language and its features (C or C++) you're going to use in order to implement your business logic. Next, another language becomes nothing more than a wrapper over logic implemented in another language. Again, if you use functions for accessing actual underlying data you'll get rid of copying this data, as you did in MyClass_GetResult method.
Here's an example
struct Object {
int a;
float b;
};
struct GroupOfObjects;
struct Result;
struct MyClass;
#ifdef __cplusplus
#include <vector>
struct GroupOfObjects {
std::vector<Object> objects;
};
struct Result {
std::vector<GroupOfObjects> groups;
};
struct MyClass {
private:
public:
Result getResult() { /*...*/ }
MyClass(int init1, int init2, float init3, const std::string& init4, const std::vector<std::string>& init5);
}
#endif
#ifdef __cplusplus
extern "C" {
#endif __cplusplus
struct Object* GroupOfObjects_GetObject(struct GroupOfObjects* g, size_t i)
/* { return &g.objects[i]; } */ // commented sections must be in cpp file, not in this header
size_t GroupOfObjects_GetCount(struct GroupOfObjects* g)
/* { return g.objects.size(); } */
struct GroupOfObjects* Result_GetGroup(struct Result* r, size_t i)
/* { return &r.groups[i]; } */
size_t Result_GetGroupCount(struct Result* r)
/* { return g.groups.size(); } */
MyClass *CreateMyClassInstance(int init1, int init2, float init3, const char* init4, const char** init5)
/* {
try {
std::vector<std::string> init5_v;
while (init5 != nullptr)
init5_v.push_back(std::string(*init5++));
return new MyClass(init1, init2, init3, std::string(init4), init5_v);
}
catch (...) {
return nullptr;
}
} */
void FreeMyClassInstance(struct MyClass* mc)
/* { delete mc; } */
Result* MyClass_GetResult(struct MyClass* mc)
/* {
Result *result = nullptr;
try {
result = new Result;
*result = mc->GetResult();
return result;
}
catch (...) {
delete result;
return nullptr;
}
} */
void FreeResult(struct Result* r)
/* { delete r; } */
#ifdef __cplusplus
} // end of extern "C"
#endif
Upvotes: 1