기본적으로 제안서를 구현했습니다. 제 질문은 완료 되었습니까? 그렇다면 어디에서? 그리고/또는 내가하는 일을하는 더 좋은 방법이 있습니까? 이 게시물의 길이에 대해 유감스럽게 생각합니다. 코드 제공 이외의 방법을 설명하는 더 좋은 방법을 알지 못했습니다.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_ptr
을 interface
안에 사용할 수 있지만, 여전히 .
표기법 호출 기능을 사용하며 실제로 포인터처럼 보이지 않으므로 사용자가 얕은 사본 일 때 사용자를 놀라게 할 수 있습니다.
X
및
X
및
Y
의 호환 쌍에 대한 해당 구현
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 모두 제대로 작동합니다.
내가 말했듯이, 어떤 제안/의견을 부탁드립니다. 그리고 이것이 완료 되었다면 (아마 내가 가진 것보다) 나에게 직접 지시 할 수 있다면 좋을 것입니다.
그것은 정말, 나에게 굉장히 복잡한 것 같다