我目前正在为一些 C++ 代码设计一个 C API 接口(这个库是预构建的)。
到目前为止,每当我需要传递一个对象时,我都会使用以下模式
public.h
struct Object;
error_code take_object( Object * object );
private.h
#include<memory>
#include "..."
struct Object {
std::shared_ptr< InternalObject > mObject;
};
impl.cpp
#include "public.h"
#include "private.h"
error_code take_object( Object * object ){ /* ... */ };
*我从示例中省略了
extern "C"
等样板
这很好用,除了我通常每次都必须提供一个明确的析构函数。然而,这个析构函数实际上具有破坏对象包装器和底层 InternalObject 的语义含义(除非它也被另一个资源使用)
但是我也有结构如下的对象
struct InteralObjectExtra {
/* ... */
}
struct InternalObject {
InternalObjectExtra data;
}
我需要公开这两个结构,但是不应显式删除 InternalObjectExtra,因为它是 InternalObject 的一部分。
我在想以下解决方案
public.h
struct Object;
#ifdef EXTERNAL
struct ObjectExtra;
#endif
/* ... */
private.h
struct Object {
std::shared_ptr< InternalObject > mObject;
};
#ifdef INTERNAL
using ObjectExtra = InteralObjectExtra;
#endif
impl.cpp(仅供参考)
ObjectExtra * get_extra( Object * object ){
return &( object->mObject.data );
}
但我不知道这是否safe(C ABI 和东西),我会假设只有指向这些类/结构的指针被传递。
我能看到的替代方案是放弃所有类型并求助于
void *
(我想避免)。
问题
我原来的解决方案安全吗?
如果没有,除了
void *
或使 ObjectExtra 成为一个包装器结构外,还有其他更好的方法,然后需要销毁它吗?
编辑
InternalObjectExtra 并不总是纯结构,但可能是模板化类型,例如
std::vector< int >
因此,简单地在 public.h 中编写
struct InternalObjectExtra
不是这些类型的选项。
为了将来参考,我采用的解决方案:
为了支持任何类型,无论是否基于模板,公开一个前向声明的结构并在内部将其转换为正确的类型。
公众:
struct vector_int;
私人:
vector_int * do_nothing(vector_int * in_vector) {
auto internal_vec = reinterpret_cast <std::vector<int> *>(in_vector);
// ... actually use the vector, like internal_vec->push_back(1);
return reinterpret_cast <vector_int *>(internal_vec);
// internal_vec is already a pointer, its value is an address.
// and we couldn't return &internal_vec, it's going out of scope so its address isn't useful
}
这类似于
void *
解决方案,但保留了键入信息。
只要有可能,我还是会使用更简单的解决方案,例如对于
std::vector<int>
,vector.data()
更好。但是,上面描述的解决方案更通用,可以在没有一个类型的干净 C 模拟的情况下应用。