如何在C语言中包装返回智能指针的C++函数?

13

我需要在C++库中将一些API封装成C语言库。我之前做过类似的事情,使用了不透明指针来表示类对象、extern "C"等,具体描述见这里。但是,我现在处理的这个新库广泛使用引用计数智能指针。我不确定如何在存在智能指针的情况下进行封装。例如,假设C++库有以下函数:

SmartPointer<MyClass> foo() {
    SmartPointer<MyClass> ret(new MyClass); // Create smart pointer ret
    ret->DoSomething(); // Do something with ret
    return ret;
}
如何在C中封装foo()? 明显,我需要一个不透明的指针(如void *或空结构指针),以便我可以在C函数中引用MyClass对象。我想到的第一个选项是从ret中提取MyClass对象,并将其转换为void *。但是,由于智能指针执行的自动删除,这个void *在ret超出范围后将变得悬空(如果我错了,请纠正我)。
另一种选择是分配一个指向智能指针的指针(例如retPtr),执行*retPtr = ret,然后创建指向retPtr的不透明指针。我认为这个选项可能有效,但这是最好的方式吗?
感谢任何帮助。

3
我同意你最后的建议。 - Sam Varshavchik
3
我不确定您需要翻译的上下文是什么,但我可以为您翻译该句子:"什么样的智能指针,我不知道 stdlib 中有叫做 SmartPointer 的类。这取决于所讨论的智能指针的语义......" - Vality
C库实际上需要访问ObjectType的成员吗?还是只是暂时持有指针然后稍后释放?另外,ObjectTypeMyClass之间有什么关系? - M.M
@Vitaly SmartPointer是我正在使用的C++库中定义的一个模板类。它是一个引用计数智能指针。我正在仔细研究SmartPointer的实现,以确保我准确理解其工作原理。 - Fijoy Vadakkumpadan
@M.M 不需要让C库知道类型 - 它只需要持有指针并根据需要将其传递给C++库API即可。ObjectType是一个笔误,抱歉 - 应该是MyClass。已更正。 - Fijoy Vadakkumpadan
显示剩余2条评论
5个回答

5

您需要返回一个不透明指针给C代码。因此,您需要使用new分配一个新的智能指针,以便您可以返回指向它的指针。

关于您唯一的选择是使用共享指针集合来保持对象的活性。当C代码表示完成该对象时,您可以将共享指针从集合中删除。这使您可以返回任何类型的标识符给C代码--它只是用作在集合中查找对象的句柄。


1
句柄的概念是一个好主意 - 毕竟这就是 C 中 FILE 的工作方式。 - Mark Ransom

4

评论表明C代码需要持有智能指针,但除了直接将其原样传递给另一个C++函数外,不需要对其进行任何操作。可以使用以下代码:

// Shared  header
#ifdef __cplusplus
extern "C" {
#endif

void * foo_acquire(void);
int  foo_bar(void *);
void foo_release(void *);

#ifdef __cplusplus
}
#endif

// C++ implementation
extern "C" void *foo_acquire()
{
    return new SmartPointer<MyClass>( foo() );
}

extern "C" int foo_bar(void *s)
{
    auto& sp = *static_cast< SmartPointer<MyClass> * >(s);
    return bar(sp);   // bar represents some function expecting the smart pointer
}  

extern "C" void foo_release(void *s)
{
    delete static_cast<SmartPointer<MyClass> *>(s);
}

这里使用了 SmartPointer 的移动构造函数 (如果没有移动构造函数则使用复制构造函数),这个操作应该由智能指针支持。

如果你想在 C 代码中防止隐式转换,可以使用一个不透明的句柄代替 void *。(例如,一个包含 void * 成员的结构体)。


2
除了David Schwartz的答案外,您可以考虑一种类似于COM的IUnknown的方法。您可以定义一个包含函数指针的结构体(在纯C中模拟C++接口),并公开一些方法,如AddRef和Release,以增加和释放引用计数。
调用者会得到该结构体的指针,因此他/她可以使用AddRef和Release来控制返回对象的正确生命周期。
此外,可以在结构体中添加其他方法(函数指针)以公开返回对象的其他功能。
这篇关于在纯C中使用COM的文章详细解释了这些内容。

0
将结构体暴露给C代码,而不仅仅是指针。在该结构体中,有指针和其状态的一些指示器。为智能指针添加行为,使其能够意识到结构体。该结构体将包含指针和其分配对象的状态。因此,智能指针的额外行为必须更新结构体中分配对象的状态(例如,在智能指针执行解除分配时将其设置为某个值)。

0

注释指出,C 代码需要保存某些东西并将 SmartPointer 传递给 C++ API,但除了逐字传递到另一个 C++ 函数之外,不需要对其执行任何操作。

我认为你需要创建类似于 std::enable_shared_from_this 的东西,比如说 EnableSharedFromThis

让你的 MyClassEnableSharedFromThis 继承:

struct MyClass : public EnableSharedFromThis, public AnotherBaseClass {
//...
};

共享头文件:

#ifdef __cplusplus
extern "C" {
#endif

struct MyClass;
MyClass * foo_acquire(void);
int  foo_bar(MyClass *);
void foo_release(MyClass *);

#ifdef __cplusplus
}
#endif

C++ 实现:

List<SmartPointer<MyClass> > listToEnsureLifeTime;
extern "C" MyClass * foo_acquire()
{
    SmartPointer<MyClass> ptr = foo();
    listToEnsureLifeTime.Add(ptr);
    return ptr.get();
}

extern "C" int foo_bar(MyClass *s)
{
    // bar represents some function expecting the smart pointer
    return bar(s->SharedFromThis());
}  

extern "C" void foo_release(MyClass *s)
{
    // I suppose this list can take difference SmartPointer with same
    // inner have the same hash or something like that
    listToEnsureLifeTime.erase(s->SharedFromThis());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接