我正在研究一些c Apis,我总是要检查一些变量是否被初始化,然后使用特殊函数清除/销毁/释放它们。如分配:
ogg_stream_state os;
ogg_stream_init(&os,ogg_page_serialno(&og));
并摧毁:
ogg_stream_clear(&os);
我想自动而不是明确地调用清理器功能。
答案 0 :(得分:2)
使用C ++模板,您可以轻松完成:
template<typename ARG, typename RET>
class Destroyer
{
public:
typedef RET (*DestoyerFn)(ARG*);
Destroyer(DestoyerFn destroyer_fn, ARG* object_ptr) { objectPointer = object_ptr; destroyerFn = destroyer_fn;}
~Destroyer()
{
if(destroyerFn && objectPointer)
destroyerFn(objectPointer);
}
private:
DestoyerFn destroyerFn;
ARG* objectPointer;
};
ARG
是您的清理函数的参数,RET
是它的返回类型(RET
需要避免编译器警告。)
示例电话:
Destroyer<ogg_stream_state, int> des_ogg_stream(ogg_stream_clear, &os);
现在每个你喜欢的地方,只需从你的功能返回,它就会调用你的清洁功能。
答案 1 :(得分:0)
在现实世界的场景中,你很可能想要围绕C函数使用某种自定义包装器来封装它们并避开C类行为和奇怪的事情,例如调用约定。
在现实世界中,我不相信您可以将任何C代码视为“通用C API”并设计一些可以处理所有可能的C API的模板类。要使这样的通用类可行,有太多事情需要考虑。
例如,给定以下随机C代码:
//cfile.c
static int* something;
void cfunction_init (void)
{
printf("C function init\n");
something = (int*) malloc(sizeof(*something));
}
void cfunction_cleanup (void)
{
printf("C function cleanup\n");
free(something);
}
你可以创建一个这样的包装类:
class wrapper
{
public:
wrapper() { cfunction_init(); }
~wrapper() { cfunction_cleanup(); }
};
然后在适当的范围内声明一个包装类变量:
#include <iostream>
int main()
{
wrapper w;
std::cout << "C++ program executing" << std::endl;
return 0;
}
节目输出:
C function init
C++ program executing
C function cleanup
答案 2 :(得分:0)
我会考虑使用自定义析构函数ogg_stream_state
包装shared_ptr
。
class OggStreamState {
public:
shared_ptr<ogg_stream_state> state;
OggStreamState() :
state(new ogg_stream_state, &ogg_stream_clear)
{}
};
您的代码现在看起来像这样:
OggStreamState os;
ogg_stream_init(os.state.get(),ogg_page_serialno(&og));
这有点难看,但这种技术为开始转向面向对象的界面提供了一个合理的位置,而不是基于C函数的界面。
例如,您可以将ogg_stream_init
移至OggStreamState
,以便它变为
OggStreamState os;
os.init(ogg_page_seialno(&og));
更进一步,重复ogg_page,你就得到了
OggPage og = ...;
OggStreamState os;
os.stream_init(og.serialno());
您甚至可以将init一直拉入构造函数
OggStreamState os(og.serialno());
或极端......
OggStreamState os(og);
这比纯粹的哨兵RAII(比如Lundin的解决方案)的另一个优点是你可以在没有麻烦的情况下将OggStreamState传入和传出函数。编译器将确定何时销毁您的上一个引用并为您调用clear函数。即你可以安全地拥有
OggStreamState oss = function_that_returns_a_stream_state(...);
当然这种技术确实引入了其他开销,但通常它们很小 - 它也会稍微模糊ogg流的所有权,这很多或者可能不是一件好事......