虽然这应该是一个微不足道的问题,但到目前为止我找不到答案。在C API中,有许多函数将指针和指针作为参数。 如何使用正确的智能指针作为C API的参数。
以下是我要转换为使用std :: unique_ptr:
的示例FMOD_SYSTEM* system = nullptr;
result = FMOD_System_Create(&system); // Create the main system object
FMOD_SOUND* musicStream;
result = FMOD_System_CreateSound(system,
musicStreamPath,
FMOD_CREATESTREAM,
nullptr,
&musicStream);
参考: FMOD_System_Create FMOD_System_CreateSound
我开始将智能指针声明为:
std::unique_ptr<FMOD_SYSTEM> system = nullptr;
std::unique_ptr<FMOD_SOUND> musicStream = nullptr;
如果我使用.get():
,这是编译器错误无法转换'std :: unique_ptr :: pointer {aka FMOD_SOUND *}'到'FMOD_SOUND **'用于参数'5'到'FMOD_RESULT FMOD_System_CreateSound(FMOD_SYSTEM *,const char *,FMOD_MODE, FMOD_CREATESOUNDEXINFO *,FMOD_SOUND **)' musicStream.get());
^
答案 0 :(得分:4)
您的问题是因为C API希望您将地址传递给FMOD_SYSTEM
指针,以便API可以用结果填充该指针 - 即,它需要FMOD_SYSTEM*
作为 out-parameter 。
在C ++中,惯用的方法是将对(智能?)指针的引用传递给FMOD_SYSTEM
,即C API为
FMOD_RESULT FMOD_System_Create(FMOD_SYSTEM **system);
FMOD_SYSTEM *system;
result = FMOD_System_Create(&system);
C ++ API将是
FMOD_RESULT FMOD_System_Create(std::unique_ptr<FMOD_SYSTEM> &system);
std::unique_ptr<FMOD_SYSTEM> system;
result = FMOD_System_Create(system);
然而,这个C ++ API存在很大问题!问题是创建 FMOD_SYSTEM
和将其包装在unique_ptr
中是一个单独的问题,不应该像这样混在一起。例如,如果我使用线程做一些聪明的事情并且真的需要FMOD_SYSTEM
来管理shared_ptr
而不仅仅是unique_ptr
怎么办?我必须创建一个unique_ptr
作为out参数传递,然后std::move
传递给shared_ptr
之后?这既丑陋又(微)低效。
std::unique_ptr<FMOD_SYSTEM> fake_system;
result = FMOD_System_Create(fake_system);
std::shared_ptr<FMOD_SYSTEM> system(std::move(fake_system));
答案是要认识到问题的根源是out-parameters本身,并且解决方案是值语义。我们想要编写的惯用C ++语法是
auto system = std::make_unique<fmod_system>();
我们获取该语法的方法是在值类中包装C API的原始指针:
class fmod_system {
FMOD_SYSTEM *ptr;
fmod_system() {
auto result = FMOD_System_Create(&ptr);
if (result != FMOD_OK) {
ptr = nullptr;
throw something;
}
}
fmod_system(fmod_system&&) = default;
fmod_system& operator=(fmod_system&&) = default;
fmod_system(const fmod_system&) = delete;
fmod_system& operator=(const fmod_system&) = delete;
~fmod_system() {
auto result = FMOD_System_Release(ptr);
assert(result == FMOD_OK); // destructors shouldn't throw: use your best judgment here
}
};
事实上,此时我们的调用者可以放弃unique_ptr
混淆并简单地写
fmod_system system;
除非他们出于某种原因需要需要额外的指针语义层。
答案 1 :(得分:3)
总的来说,我不相信将智能指针与C-API混合是一个好主意,更好的方法是将C-API包装在C ++类中,如Quuxplusone建议的那样。
然而,要回答你的问题,&#34;最干净的&#34; (但仍然是丑陋的方式)我能想到的是在创建对象之后将原始指针传递给带有自定义删除器的智能指针。
struct FMOD_SYSTEM_Deleter {
void operator()(FMOD_SYSTEM* sys) {
if (sys != nullptr) {
FMOD_System_Release(sys);
}
}
}
FMOD_SYSTEM* tsys = nullptr;
result = FMOD_System_Create(&tsys);
std::unique_ptr<FMOD_SYSTEM,FMOD_SYSTEM_Deleter> system(tsys);
答案 2 :(得分:2)
简单的答案是:C ++智能指针用于您使用new
和delete
创建的自我分配对象。
它们不是为指向对象的指针构建的,这些对象是由第三方C风格API函数内部创建的,需要由另一个C Style API函数发布。
在您的情况下,您必须致电System::release
而不是delete
,因此使用智能指针可能会导致严重问题。
您有三种可能性: