基于OO的C ++库的C和C ++接口

时间:2019-01-07 18:17:44

标签: c++ c

我目前正在使用C ++开发一个库(主要是因为从属库具有C ++接口)。我创建了带有C ++接口的概念验证实现,以实现快速开发。该库必须强制为其提供C接口。 C ++接口很好,但如果妨碍了C接口,则可以对其进行修改/删除。

C ++ API现在看起来像这样:

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 &params) = 0;
    virtual bool getResult(Result &result) = 0;
    //Other public methods, constructor, virtual destructor
}

// Actual implementation of the above interface
class MyClass : IMyClass {
}

IMyClass *createMyClassInstance();

到目前为止,我已经提出了这个C接口:

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" 

C接口的实现

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
}

我对此有一些疑问:

  1. GetResult方法的开销是将对象从C ++向量复制到C数组。有没有更优雅,更有效的方式来处理此问题?
  2. 我将必须维护C和C ++的结构。我应该只在C ++接口中使用MyClassInitParams和Result结构的C版本吗?这也将有助于(1)。
  3. 如果我使用(2)中的解决方案,那么即使拥有C ++接口也有意义吗?还是在这种情况下同时保留C和C ++接口有什么好处?

1 个答案:

答案 0 :(得分:1)

  1. 我建议从C MyClass_GetResult方法返回Result *,例如int MyClass_GetResult(MyClassHandle handle, Result_C **result)Result_C* MyClass_GetResult(MyClassHandle handle)。然后,为组和对象添加访问器。

  2. 由您决定,但我希望使用其中一个,但不能同时使用。

  3. 首先,我建议您决定要使用哪种语言及其功能(C或C ++)来实现业务逻辑。接下来,另一种语言无非是对用另一种语言实现的逻辑的包装。同样,如果您使用函数来访问实际的基础数据,则将像在MyClass_GetResult方法中一样,无需复制这些数据。

这是一个例子

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