仅标头库循环依赖

时间:2013-01-08 14:23:39

标签: c++ circular-dependency raii

我正在尝试围绕外部C API创建一个仅限标头的C ++库。 C API使用void *指针作为句柄。 这是个主意:

// resource.hpp
class Resource {
public:
    // RAII constructor, destructor, etc.
    // ...
    void do_something(const Handle & h) {
        do_something_impl( (void *) h);
    }
};

// handle.hpp
class Handle
{
public:
    Handle(size_t n, const Resource & res)
        : p_(res.allocate(n)), res_(res) {}

    // cast operation
    operator void *() const { return p_; }

private:
    void * p_;
    Resource & res_;
};

这里的问题是(a)Handle必须保持对Resource的引用,以及(b)Resource需要能够将Handle转换为void *。不幸的是,这导致了循环依赖。

关于如何重组的任何想法?

注意:答案不是简单地“包含xxx.hpp”或转发声明其中一个类。 这需要以某种方式重组,我只是不知道如何。

class Handle作为前向声明添加到资源文件的顶部不起作用,因为(void *)强制转换是资源仍无法看到的句柄定义的一部分。同样,将强制转换更改为void * ptr()成员函数会导致同样的问题。

将函数定义移动到.cpp文件也不是答案 - 它只需要是标题。

2 个答案:

答案 0 :(得分:3)

嗯,这是拯救的模板(再次!):

// resource.hpp
class Resource;
template<typename TResource> class Handle;

class Resource {
public:
    // RAII constructor, destructor, etc.
    // ...
    void do_something(const Handle<Resource> & h) {
        do_something_impl( (void *) h);
    }
};

// handle.hpp
template<class TResource>
class Handle {
public:
    Handle(size_t n, const TResource & res)
        : p_(res.allocate(n)), res_(res) {}

    // cast operation
    operator void *() const { return p_; }

private:
    void * p_;
    TResource & res_;
};

答案 1 :(得分:1)

不要将头文件相互包含在一起,而是要有一个前向声明类。这样它们就可以用作引用或指针。

所以在resource.hpp中:

class Handle;

class Resource {
public:
    // RAII constructor, destructor, etc.
    // ...
    void do_something(const Handle & h) {
        do_something_impl( (void *) h);
    }
};

在handle.hpp中:

class Resource;

class Handle
{
public:
    Handle(size_t n, const Resource & res)
        : p_(res.allocate(n)), res_(res) {}

    // cast operation
    operator void *() const { return p_; }

private:
    void * p_;
    Resource & res_;
};

由于您在void*函数中使用do_something类型转换操作符,因此必须将该实现移动到单独的源文件中。在该源文件中,您可以包含两个头文件,因此可以访问所有功能。