2011-04-14 3 views
2

기본적으로 제안서를 구현했습니다. 제 질문은 완료 되었습니까? 그렇다면 어디에서? 그리고/또는 내가하는 일을하는 더 좋은 방법이 있습니까? 이 게시물의 길이에 대해 유감스럽게 생각합니다. 코드 제공 이외의 방법을 설명하는 더 좋은 방법을 알지 못했습니다.Pimpl 프레임 워크 의견/제안 요청

나는 이전에 기본적으로, 우리는 인터페이스 interface 및 구현 impl있어 말할 수, 여기서 다시 그 질문을 설명하기 위해 질문 pimpl: Avoiding pointer to pointer with pimpl?

물었다. 더구나, pimpl 관용구처럼, 우리는 impl을 개별적으로 컴파일 할 수 있기를 원합니다.

이제 C++ 0x에서이 작업을 수행하는 방법은 impl을 가리키는 interface의 말을 unique_ptr으로 만드는 것입니다. 실제로 구현되는 interface의 메소드는 main.cpp에 포함되지 않으며 impl의 인터페이스와 구현과 함께 interface.cpp으로 따로 컴파일됩니다.

이 클래스는 포인터가없는 것처럼 디자인하여 사용자에게 효과적으로 전달합니다. -> 표기법이 아닌 . 표기법을 사용하여 메소드를 호출합니다. 복사하려는 경우 딥 복사 구조도를 구현합니다.

하지만이 pimpl에 대한 공유 포인터를 실제로 원하면 어떻게 될지 생각했습니다. 나는 단지 shared_ptr<interface>을 할 수 있었지만, 그렇다면 unique_ptr에 shared_ptr을 주었을 것입니다. 그리고 저는 그것이 바보 같다고 생각했습니다. 나는 unique_ptr 대신 shared_ptrinterface 안에 사용할 수 있지만, 여전히 . 표기법 호출 기능을 사용하며 실제로 포인터처럼 보이지 않으므로 사용자가 얕은 사본 일 때 사용자를 놀라게 할 수 있습니다.

은 내가 pimpl의 상용구 물건을 많이 취급,이 인터페이스 XXY의 호환 쌍에 대한 해당 구현 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 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 

우리는 우리의 경우 impl에있는 템플릿 클래스 INTERFACE (우리가 unique_pimpl 여기에 채울 하나 개의 매개 변수, HANDLER을 가지고있는), 그리고 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로 돌아 올 수 있습니다 :이 모든 적절한 템플릿 instantate_my_unique_pimpl_create_functions() 우리가 0 인수가 만들고 컴파일하고, 그렇지 않으면 호출하도록 구성되지 않습니다 보장 컴파일 확인합니다

#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(); 
    } 
} 

. 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 : shared_pimpl에 대한 create 실제로 이중 리디렉션하지 않고 실제 shared_ptr을 반환

#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 

주 . get_impl()의 static_cast는 혼란 스럽습니다. 슬프게도 저는 상속 트리에서 두 단계 위로 이동 한 다음 구현으로 이동하는 것보다 더 나은 방법을 알지 못했습니다.

예를 들어 침입자 포인터에 대해 다른 "HANDLER"클래스를 만들거나 모든 헤더 파일을 기존 방식으로 포함해야하는 간단한 스택 할당 조인을 생각할 수 있습니다. 그렇게하면 사용자는 pimpl 준비가되었지만 pimpl이 필요하지 않은 클래스를 작성할 수 있습니다.

모든 파일은 우편 번호 here에서 다운로드 할 수 있습니다. 그들은 현재 디렉토리로 추출합니다. 몇 가지 C++ 0x 기능을 사용하여 컴파일해야합니다. gcc 4.4.5와 gcc 4.6.0 모두 제대로 작동합니다.

내가 말했듯이, 어떤 제안/의견을 부탁드립니다. 그리고 이것이 완료 되었다면 (아마 내가 가진 것보다) 나에게 직접 지시 할 수 있다면 좋을 것입니다.

그것은 정말, 나에게 굉장히 복잡한 것 같다

답변

3

...

당신이 두 배 "인터페이스"를 정의 할 필요가 제안한 . 의미

다음에 대해 한 번 Proxy
  • 에 대해 한 번

    • 을 기본 클래스

    너무 적은 이익 때문에 DRY의 직접적인 위반입니다!

    공유 소유권이있는 경우 클래스를 사용하는 데 많은 이점이 보이지 않습니다. 간단히 std::shared_ptr입니다.

    이 나 자신이 pimpl 구현 템플릿을 쓴 이유 중 하나이며, 불완전 유형 값의 의미를 얻을 수 있도록이는 shared_ptrDeleter가 구현 + 깊은 복사의 의미를 적응을 위해이었다.

    코드에 도우미의 레이어를 추가하면 찾아보기가 더 어려워집니다.