如何隐式调用c风格的清理函数?

时间:2015-06-10 04:16:33

标签: c++ c variables free destroy

我正在研究一些c Apis,我总是要检查一些变量是否被初始化,然后使用特殊函数清除/销毁/释放它们。如分配:

ogg_stream_state os;
ogg_stream_init(&os,ogg_page_serialno(&og));

并摧毁:

ogg_stream_clear(&os);

我想自动而不是明确地调用清理器功能。

3 个答案:

答案 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流的所有权,这很多或者可能不是一件好事......