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

时间:2022-10-11 20:27:08

I need to wrap some APIs in a C++ library in C. I have done this in the past, using opaque pointers to class objects, extern "C", etc. as described here. However, this new library I'm dealing with makes extensive use of reference-counting smart pointers. I'm not sure how to do the wrapping in the presence of smart pointers. For example, let's say the C++ library has the following function:

我需要在C中的C ++库中包装一些API。我过去已经使用了对类对象的不透明指针,extern“C”等,如此处所述。但是,我正在处理的这个新库大量使用引用计数智能指针。我不确定如何在有智能指针的情况下进行包装。例如,假设C ++库具有以下功能:

SmartPointer<MyClass> foo() {
    SmartPointer<MyClass> ret(new MyClass); // Create smart pointer ret
    ret->DoSomething(); // Do something with ret
    return ret;
}

How can I wrap foo() in C? Clearly, I need an opaque pointer (like void* or an empty struct pointer) that I can use in a C function to refer to the MyClass object. The first option I thought of was to extract the MyClass object from ret, and cast it into a void*. However, this void* will become dangling once ret goes out of scope due to the automatic deletion done by the smart pointer (correct me if I'm wrong).

如何在C中包装foo()?显然,我需要一个不透明的指针(如void *或一个空结构指针),我可以在C函数中使用它来引用MyClass对象。我想到的第一个选项是从ret中提取MyClass对象,并将其转换为void *。然而,由于智能指针自动删除(如果我错了,请纠正我),一旦ret超出范围,这个void *将变为悬空。

The other option is to allocate a pointer to the smart pointer (say retPtr), do *retPtr=ret, and then create an opaque pointer to retPtr. I think this option might work, but is this the best way?

另一个选项是分配一个指向智能指针的指针(比如retPtr),do * retPtr = ret,然后创建一个指向retPtr的不透明指针。我认为这个选项可能有用,但这是最好的方法吗?

Any help is appreciated.

任何帮助表示赞赏。

5 个解决方案

#1


1  

The comments indicate that the C code needs to hold the smart pointer but does not need to do anything with it other than pass it verbatim to another C++ function. The code for that could be:

注释表明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);
}

This uses the move-constructor of SmartPointer (or copy-constructor if it didn't have a move-constructor), an operation that should be supported by smart pointers.

这使用了SmartPointer的move-constructor(如果它没有move-constructor,则使用copy-constructor),这是智能指针应该支持的操作。

If you want to prevent implicit conversions in the C code, you could use an opaque handle instead of void *. (E.g. a struct with a void * as member).

如果要防止C代码中的隐式转换,可以使用opaque句柄而不是void *。 (例如,具有void *作为成员的结构)。

#2


5  

You pretty much have to return an opaque pointer to the C code. So you'll need to use new to allocate a new smart pointer that you can return a pointer to.

你几乎必须返回一个指向C代码的不透明指针。因此,您需要使用new来分配一个可以返回指针的新智能指针。

About your only alternative is to use a collection of shared pointers to keep the object alive. When the C code indicates it's finished with the object, you remove the shared pointer from the collection. This allows you to return any kind of identifier you want to the C code -- it's just used as a handle to find the object in the collection.

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

#3


2  

In addition to David Schwartz's answer, you may consider an approach similar to COM's IUnknown. You may define a struct containing function pointers (simulating a C++ interface in pure C), and expose a couple of methods like AddRef and Release, to increase and release the ref count.

除了David Schwartz的回答,你可以考虑一种类似于COM的IUnknown的方法。您可以定义一个包含函数指针的结构(在纯C中模拟C ++接口),并公开一些方法,如AddRef和Release,以增加和释放引用计数。

The caller gets a pointer to that structure, so he/she can control the proper lifetime of the returned object using AddRef and Release.

调用者获取指向该结构的指针,因此他/她可以使用AddRef和Release控制返回对象的正确生命周期。

In addition, other methods (function pointers) can be added in the struct to expose other functionalities of the returned object.

此外,可以在结构中添加其他方法(函数指针)以公开返回对象的其他功能。

This article on COM in plain C explains things in detail.

这篇关于普通C中COM的文章详细解释了一些事情。

#4


0  

Expose a struct to the C code rather than just the pointer. In the said struct, have the pointer and some indicator of its state. Add behavior to the smart pointer so that it is aware of the struct. The struct would contain both a pointer and the state of its allocated object. So the smart pointer's extra behavior would have to update the state of the allocated object in the struct (for example, setting it to some value when the smart pointer does the deallocation).

将结构暴露给C代码而不仅仅是指针。在所述结构中,具有指针和其状态的一些指示符。向智能指针添加行为,以便它知道结构。结构将包含指针和其分配对象的状态。因此,智能指针的额外行为必须更新结构中已分配对象的状态(例如,在智能指针执行重新分配时将其设置为某个值)。

#5


0  

The comments indicate that the C code needs to hold something and pass the SmartPointer to C++ API but does not need to do anything with it other than pass it verbatim to another C++ function.

注释表明C代码需要保存并将SmartPointer传递给C ++ API,但除了将其逐字传递给另一个C ++函数之外,不需要对它做任何事情。

I think you need to create some think like std::enable_shared_from_this, let's say EnableSharedFromThis:

我认为你需要创建一些思考像std :: enable_shared_from_this,让我们说EnableSharedFromThis:

Make your MyClass inherite from EnableSharedFromThis:

使您的MyClass继承自EnableSharedFromThis:

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

Shared header:

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

C++ implementation:

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());
}

#1


1  

The comments indicate that the C code needs to hold the smart pointer but does not need to do anything with it other than pass it verbatim to another C++ function. The code for that could be:

注释表明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);
}

This uses the move-constructor of SmartPointer (or copy-constructor if it didn't have a move-constructor), an operation that should be supported by smart pointers.

这使用了SmartPointer的move-constructor(如果它没有move-constructor,则使用copy-constructor),这是智能指针应该支持的操作。

If you want to prevent implicit conversions in the C code, you could use an opaque handle instead of void *. (E.g. a struct with a void * as member).

如果要防止C代码中的隐式转换,可以使用opaque句柄而不是void *。 (例如,具有void *作为成员的结构)。

#2


5  

You pretty much have to return an opaque pointer to the C code. So you'll need to use new to allocate a new smart pointer that you can return a pointer to.

你几乎必须返回一个指向C代码的不透明指针。因此,您需要使用new来分配一个可以返回指针的新智能指针。

About your only alternative is to use a collection of shared pointers to keep the object alive. When the C code indicates it's finished with the object, you remove the shared pointer from the collection. This allows you to return any kind of identifier you want to the C code -- it's just used as a handle to find the object in the collection.

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

#3


2  

In addition to David Schwartz's answer, you may consider an approach similar to COM's IUnknown. You may define a struct containing function pointers (simulating a C++ interface in pure C), and expose a couple of methods like AddRef and Release, to increase and release the ref count.

除了David Schwartz的回答,你可以考虑一种类似于COM的IUnknown的方法。您可以定义一个包含函数指针的结构(在纯C中模拟C ++接口),并公开一些方法,如AddRef和Release,以增加和释放引用计数。

The caller gets a pointer to that structure, so he/she can control the proper lifetime of the returned object using AddRef and Release.

调用者获取指向该结构的指针,因此他/她可以使用AddRef和Release控制返回对象的正确生命周期。

In addition, other methods (function pointers) can be added in the struct to expose other functionalities of the returned object.

此外,可以在结构中添加其他方法(函数指针)以公开返回对象的其他功能。

This article on COM in plain C explains things in detail.

这篇关于普通C中COM的文章详细解释了一些事情。

#4


0  

Expose a struct to the C code rather than just the pointer. In the said struct, have the pointer and some indicator of its state. Add behavior to the smart pointer so that it is aware of the struct. The struct would contain both a pointer and the state of its allocated object. So the smart pointer's extra behavior would have to update the state of the allocated object in the struct (for example, setting it to some value when the smart pointer does the deallocation).

将结构暴露给C代码而不仅仅是指针。在所述结构中,具有指针和其状态的一些指示符。向智能指针添加行为,以便它知道结构。结构将包含指针和其分配对象的状态。因此,智能指针的额外行为必须更新结构中已分配对象的状态(例如,在智能指针执行重新分配时将其设置为某个值)。

#5


0  

The comments indicate that the C code needs to hold something and pass the SmartPointer to C++ API but does not need to do anything with it other than pass it verbatim to another C++ function.

注释表明C代码需要保存并将SmartPointer传递给C ++ API,但除了将其逐字传递给另一个C ++函数之外,不需要对它做任何事情。

I think you need to create some think like std::enable_shared_from_this, let's say EnableSharedFromThis:

我认为你需要创建一些思考像std :: enable_shared_from_this,让我们说EnableSharedFromThis:

Make your MyClass inherite from EnableSharedFromThis:

使您的MyClass继承自EnableSharedFromThis:

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

Shared header:

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

C++ implementation:

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());
}