2011-04-14 86 views
2

我已经基本实现了一个提案,我的问题是,它已经完成了,如果是的话,在哪里?和/或有更好的方式来做我在做什么?对于这篇文章的长度感到抱歉,我不知道有更好的方式来解释我的方法,而不是提供代码。Pimpl框架评论/建议要求

我以前问的问题pimpl: Avoiding pointer to pointer with pimpl?

要在这里再次解释这个问题,基本上,可以说,我们已经有了一个接口interface和实施impl。此外,像pimpl成语,我们希望能够单独编译impl

现在在C++ 0x中做到这一点的一种方法是在interface中发出unique_ptr指向implinterface的方法的实际实现不包括在main.cpp中,它们分别在interface.cpp中被单独编译,以及impl的接口和实现。

我们设计这个类就好像指针不存在一样,所以它对用户来说是有效透明的。我们使用.表示法调用方法,而不是->表示法,如果我们想要复制,我们实现深度复制原理图。

但是后来我在想如果我真的想要一个共享指针到这个pimpl。我可以做shared_ptr<interface>,但是我有一个shared_ptr到unique_ptr,我认为这有点傻。我可以使用shared_ptr而不是unique_ptrinterface之内,但是它仍然会使用.表示法调用函数,并且看起来不像指针,所以在浅拷贝时可能会让用户感到惊讶。

我开始认为这将是很好的有一个连接的接口X和任何兼容对XY一个对应的实现Y一些通用的模板类,处理了大量的平普尔样板东西。

下面是我试图去做的。

首先,我将与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平普尔实施。 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_pimpl。但是,我们可以创建其他的连接方法,这里是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 

注意createshared_pimpl实际上返回一个真正的shared_ptr,没有双重重定向。在get_impl()中的static_cast是一团糟,遗憾的是我不知道有更好的方法来做到这一点,除了继承树和继承树的两个步骤之外。

例如,我可以想象为其他“HANDLER”类指定入侵指针,甚至是一个简单的堆栈分配连接,它需要以传统方式包含所有头文件。这样用户可以编写pimpl就绪但不需要pimpl的类。

您可以从zip here下载所有文件。他们将提取到当前目录。你需要用某些C++ 0x特性进行编译,gcc 4.4.5和gcc 4.6.0对我来说都很好。

所以就像我说的,任何建议/意见将不胜感激,如果这已经完成(可能比我更好),如果你可以指示我,它会很好。

回答

3

,真的,看起来太过复杂,我...

你提出要求来定义“接口”两次.语义:

  • 一旦你Proxy
  • 曾经为基类

这是DRY的直接违反,只有很少的收益!

在使用您的课程时,我看不到太多的要点,只是在共享所有权的情况下使用std::shared_ptr

还有一个原因,我自己写了一个PIMPL实现模板,这是为适应shared_ptr缺失者实现+深度复制语义,从而获得不完全类型的值语义。

在代码中添加层帮手最终导致浏览更加困难。