【发布时间】:2011-04-14 16:29:27
【问题描述】:
我基本上已经实现了一个提案,我的问题是,它是否已经完成,如果是,在哪里?和/或有更好的方法来做我正在做的事情吗?很抱歉这篇文章的长度,除了提供代码之外,我不知道有更好的方法来解释我的方法。
我之前问过pimpl: Avoiding pointer to pointer with pimpl?的问题
在这里再次解释这个问题,基本上,假设我们有一个接口interface 和一个实现impl。此外,像 pimpl 成语一样,我们希望能够单独编译 impl。
现在在 c++0x 中执行此操作的一种方法是在指向 impl 的 interface 中声明 unique_ptr。 interface的方法的实际实现不包含在main.cpp中,它们分别编译在interface.cpp中,以及impl的接口和实现。
我们设计这个类,就好像指针不存在一样,因此它对用户有效地透明。我们使用.表示法调用方法,而不是->表示法,如果我们要复制,我们实现深拷贝原理图。
但后来我在想,如果我真的想要一个指向这个 pimpl 的共享指针怎么办。我可以只做shared_ptr<interface>,但是我会有一个shared_ptr 到一个unique_ptr,我觉得这有点傻。我可以在interface 中使用shared_ptr 而不是unique_ptr,但是它仍然会使用. 表示法调用函数,并且看起来不像一个指针,所以当它浅拷贝时可能会让用户感到惊讶。
我开始认为最好有一些通用模板类来连接接口X 和对应的实现Y 用于任何兼容的X 和Y 对,处理很多pimpl样板的东西。
下面是我尝试的方法。
首先我从main.cpp开始:
#include "interface.hpp"
#include "unique_pimpl.hpp"
#include "shared_pimpl.hpp"
int main()
{
auto x1 = unique_pimpl<interface, impl>::create();
x1.f();
auto x2(x1);
x2 = x1;
auto x3(std::move(x1));
x3 = std::move(x1);
auto y1 = shared_pimpl<interface, impl>::create();
y1->f();
auto y2(y1);
y2 = y1;
auto y3(std::move(y1));
y3 = std::move(y1);
}
基本上在这里,x1 是标准的unique_ptr pimpl 实现。 x2实际上是一个shared_ptr,没有unique_ptr引起的双指针。很多赋值和构造函数仅用于测试。
现在interface.hpp:
#ifndef INTERFACE_HPP
#define INTERFACE_HPP
#include "interface_macros.hpp"
class impl;
INTERFACE_START(interface);
void f();
INTERFACE_END;
#endif
interface_macros.hpp:
#ifndef INTERFACE_MACROS_HPP
#define INTERFACE_MACROS_HPP
#include <utility>
#define INTERFACE_START(class_name) \
template <class HANDLER> \
class class_name : public HANDLER \
{ \
public: \
class_name(HANDLER&& h = HANDLER()) : HANDLER(std::move(h)) {} \
class_name(class_name<HANDLER>&& x) : HANDLER(std::move(x)) {} \
class_name(const class_name<HANDLER>& x) : HANDLER(x) {}
#define INTERFACE_END }
#endif
interface_macros.hpp 仅包含我开发的框架所需的一些样板代码。该接口将HANDLER 作为模板参数并使其成为基类,此构造函数只是确保将事物转发到发生操作的基HANDLER。当然interface 本身没有成员,也没有构造函数,因为它是故意的,只有一些公共成员函数。
现在interface.cpp 是我们的另一个文件。它实际上包含interface 的实现,尽管它的名字,也包含impl 的接口和实现。我不会完整列出该文件,但首先认为它包含的是interface_impl.hpp(抱歉命名混乱)。
这里是interface_impl.hpp:
#ifndef INTERFACE_IMPL_HPP
#define INTERFACE_IMPL_HPP
#include "interface.hpp"
#include "impl.hpp"
template <class HANDLER>
void interface<HANDLER>::f() { this->get_impl().f(); }
#endif
注意get_impl() 方法调用。这将由HANDLER 稍后提供。
impl.hpp 包含impl 的接口和实现。我本可以将这些分开,但没有看到需要。这里是impl.hpp:
#ifndef IMPL_HPP
#define IMPL_HPP
#include "interface.hpp"
#include <iostream>
class impl
{
public:
void f() { std::cout << "Hello World" << std::endl; };
};
#endif
现在让我们看看unique_pimpl.hpp。请记住,这已包含在 main.cpp 中,因此我们的主程序对此进行了定义。
unique_pimpl.hpp:
#ifndef UNIQUE_PIMPL_HPP
#define UNIQUE_PIMPL_HPP
#include <memory>
template
<
template<class> class INTERFACE,
class IMPL
>
class unique_pimpl
{
public:
typedef IMPL impl_type;
typedef unique_pimpl<INTERFACE, IMPL> this_type;
typedef INTERFACE<this_type> super_type;
template <class ...ARGS>
static super_type create(ARGS&& ...args);
protected:
unique_pimpl(const this_type&);
unique_pimpl(this_type&& x);
this_type& operator=(const this_type&);
this_type& operator=(this_type&& p);
~unique_pimpl();
unique_pimpl(impl_type* p);
impl_type& get_impl();
const impl_type& get_impl() const;
private:
std::unique_ptr<impl_type> p_;
};
#endif
这里我们将传递模板类INTERFACE(它有一个参数HANDLER,我们将在此处填写unique_pimpl)和IMPL类(在我们的例子中是impl )。此类是unique_ptr 实际所在的位置。
现在这里提供了我们正在寻找的get_impl() 函数。我们的接口可以调用此函数,以便将调用转发到实现。
让我们看看unique_pimpl_impl.hpp:
#ifndef UNIQUE_PIMPL_IMPL_HPP
#define UNIQUE_PIMPL_IMPL_HPP
#include "unique_pimpl.hpp"
#define DEFINE_UNIQUE_PIMPL(interface, impl, type) \
template class unique_pimpl<interface, impl>; \
typedef unique_pimpl<interface, impl> type; \
template class interface< type >;
template < template<class> class INTERFACE, class IMPL> template <class ...ARGS>
typename unique_pimpl<INTERFACE, IMPL>::super_type
unique_pimpl<INTERFACE, IMPL>::create(ARGS&&... args)
{ return unique_pimpl<INTERFACE, IMPL>::super_type(new IMPL(std::forward<ARGS>(args)...)); }
template < template<class> class INTERFACE, class IMPL>
typename unique_pimpl<INTERFACE, IMPL>::impl_type&
unique_pimpl<INTERFACE, IMPL>::get_impl()
{ return *p_; }
template < template<class> class INTERFACE, class IMPL>
const typename unique_pimpl<INTERFACE, IMPL>::impl_type&
unique_pimpl<INTERFACE, IMPL>::get_impl() const
{ return *p_; }
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>::unique_pimpl(typename unique_pimpl<INTERFACE, IMPL>::impl_type* p)
: p_(p) {}
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>::~unique_pimpl() {}
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>::unique_pimpl(unique_pimpl<INTERFACE, IMPL>&& x) :
p_(std::move(x.p_)) {}
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>::unique_pimpl(const unique_pimpl<INTERFACE, IMPL>& x) :
p_(new IMPL(*(x.p_))) {}
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>& unique_pimpl<INTERFACE, IMPL>::operator=(unique_pimpl<INTERFACE, IMPL>&& x)
{ if (this != &x) { (*this).p_ = std::move(x.p_); } return *this; }
template < template<class> class INTERFACE, class IMPL>
unique_pimpl<INTERFACE, IMPL>& unique_pimpl<INTERFACE, IMPL>::operator=(const unique_pimpl<INTERFACE, IMPL>& x)
{ if (this != &x) { this->p_ = std::unique_ptr<IMPL>(new IMPL(*(x.p_))); } return *this; }
#endif
现在上面的很多只是样板代码,并且可以满足您的期望。 create(...) 只是转发给impl 的构造函数,否则用户将看不到它。还有一个宏定义DEFINE_UNIQUE_PIMPL,我们稍后可以使用它来实例化适当的模板。
现在我们可以回到interface.cpp:
#include "interface_impl.hpp"
#include "unique_pimpl_impl.hpp"
#include "shared_pimpl_impl.hpp"
// This instantates required functions
DEFINE_UNIQUE_PIMPL(interface, impl, my_unique_pimpl)
namespace
{
void instantate_my_unique_pimpl_create_functions()
{
my_unique_pimpl::create();
}
}
DEFINE_SHARED_PIMPL(interface, impl, my_shared_pimpl)
namespace
{
void instantate_my_shared_pimpl_create_functions()
{
my_shared_pimpl::create();
}
}
这确保编译所有适当的模板instantate_my_unique_pimpl_create_functions() 确保我们编译一个 0 参数创建,否则永远不会被调用。如果 impl 有其他我们想从 main 调用的构造函数,我们可以在这里定义它们(例如 my_unique_pimpl::create(int(0)))。
回顾main.cpp,您现在可以看到如何创建unique_pimpls。但是我们可以创建其他的加入方法,这里是shared_pimpl:
shared_pimpl.hpp:
#ifndef SHARED_PIMPL_HPP
#define SHARED_PIMPL_HPP
#include <memory>
template <template<class> class INTERFACE, class IMPL>
class shared_impl_handler;
template < template<class> class INTERFACE, class IMPL>
class shared_pimpl_get_impl
{
public:
IMPL& get_impl();
const IMPL& get_impl() const;
};
template
<
template<class> class INTERFACE,
class IMPL
>
class shared_pimpl
{
public:
typedef INTERFACE< shared_pimpl_get_impl<INTERFACE, IMPL> > interface_type;
typedef shared_impl_handler<INTERFACE, IMPL> impl_type;
typedef std::shared_ptr<interface_type> return_type;
template <class ...ARGS>
static return_type create(ARGS&& ...args);
};
#endif
shared_pimpl_impl.hpp:
#ifndef SHARED_PIMPL_IMPL_HPP
#define SHARED_PIMPL_IMPL_HPP
#include "shared_pimpl.hpp"
#define DEFINE_SHARED_PIMPL(interface, impl, type) \
template class shared_pimpl<interface, impl>; \
typedef shared_pimpl<interface, impl> type; \
template class interface< shared_pimpl_get_impl<interface, impl> >;
template <template<class> class INTERFACE, class IMPL>
class shared_impl_handler : public INTERFACE< shared_pimpl_get_impl<INTERFACE, IMPL> >, public IMPL
{
public:
template <class ...ARGS>
shared_impl_handler(ARGS&&... args) : INTERFACE< shared_pimpl_get_impl<INTERFACE, IMPL> >(), IMPL(std::forward<ARGS>(args)...) {}
};
template < template<class> class INTERFACE, class IMPL> template <class ...ARGS>
typename shared_pimpl<INTERFACE, IMPL>::return_type shared_pimpl<INTERFACE, IMPL>::create(ARGS&&... args)
{ return shared_pimpl<INTERFACE, IMPL>::return_type(new shared_pimpl<INTERFACE, IMPL>::impl_type(std::forward<ARGS>(args)...)); }
template < template<class> class INTERFACE, class IMPL>
IMPL& shared_pimpl_get_impl<INTERFACE, IMPL>::get_impl()
{ return static_cast<IMPL&>(static_cast<shared_impl_handler<INTERFACE, IMPL>& >(static_cast<INTERFACE< shared_pimpl_get_impl<INTERFACE, IMPL> >&>(*this))); }
template < template<class> class INTERFACE, class IMPL>
const IMPL& shared_pimpl_get_impl<INTERFACE, IMPL>::get_impl() const
{ return static_cast<const IMPL&>(static_cast<const shared_impl_handler<INTERFACE, IMPL>& >(static_cast<const INTERFACE<shared_pimpl_get_impl<INTERFACE, IMPL> >&>(*this))); }
#endif
请注意,shared_pimpl 的 create 实际上返回一个真正的 shared_ptr,没有双重重定向。 get_impl() 中的 static_cast 是一团糟,遗憾的是我不知道更好的方法,除了在继承树上走两步,然后再往下走。
例如,我可以想象为侵入式指针制作其他“HANDLER”类,甚至是一个简单的堆栈分配连接,它需要以传统方式包含所有头文件。这样用户就可以编写 pimpl 就绪但不需要 pimpl 的类。
您可以从 zip here 下载所有文件。他们将解压到当前目录。你需要用一些 c++0x 特性来编译,gcc 4.4.5 和 gcc 4.6.0 对我来说都很好。
所以就像我说的,任何建议/cmets 将不胜感激,如果这已经完成(可能比我做得更好),如果你能指导我,那就太好了。
【问题讨论】:
标签: c++ templates c++11 pimpl-idiom