2011-04-14 53 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缺失者實現+深度複製語義,從而獲得不完全類型的值語義。

在代碼中添加層幫手最終導致瀏覽更加困難。