请求Pimpl框架意见/建议
Pimpl framework comments/suggestions requested
我基本上已经实现了一个提案,我的问题是,它完成了吗?如果完成了,在哪里?和/或有更好的方法来做我正在做的事情吗?很抱歉这篇文章太长了,除了提供代码之外,我不知道还有什么更好的方法来解释我的方法。
我之前问过皮条客这个问题:避免与皮条客进行指针对指针?
为了在这里再次解释这个问题,基本上,假设我们有一个接口interface
和一个实现impl
。此外,就像皮条习惯用法一样,我们希望能够单独编译impl
。
现在,在c++0x中这样做的一种方法是在指向impl
的interface
中生成一个例如unique_ptr
。实际实现interface
的方法没有包含在main.cpp
中,它们在interface.cpp
中单独编译,以及impl
的接口和实现。
我们将这个类设计为指针不在那里,这样它对用户来说是有效透明的。我们使用.
表示法来调用方法,而不是->
表示法,如果我们想要复制,我们实现深度复制示意图。
但后来我在想,如果我真的想要一个指向这个皮条客的共享指针怎么办。我可以只做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
稍后提供。
CCD_ 41包含了CCD_ 42的接口和实现。我本可以把这些分开,但我觉得没有必要。这里是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
注意,shared_pimpl
的create
实际上返回了一个真实的shared_ptr
,而没有双重重定向。get_impl()
中的static_cast一团糟,遗憾的是,除了在继承树上走两步,然后再往下走一步,我不知道还有什么更好的方法可以做到这一点。
例如,我可以想象为侵入指针创建其他"HANDLER"类,甚至是一个简单的堆栈分配联接,它要求以传统方式包含所有头文件。通过这种方式,用户可以编写准备好但不需要皮条的类。
你可以在这里下载所有文件。它们将提取到当前目录。您需要使用一些具有c++0x功能的东西进行编译,gcc 4.4.5和gcc 4.6.0对我来说都很好
因此,正如我所说,任何建议/意见都将不胜感激,如果已经做到了(可能比我做得更好),如果你能指导我去做,那就太好了。
对我来说,这真的非常复杂…
您提出的.
语义需要定义两次"接口":
Proxy
一次- 基类一次
这是对DRY的直接违反,收益如此之少!
在共享所有权的情况下,我认为使用您的类而不是使用std::shared_ptr
没有多大意义。
有一个原因是我自己写了一个pimpl实现模板,这是为了适应shared_ptr
deleter实现+深度复制语义,从而获得不完整类型的值语义。
在代码中添加助手的层最终会使浏览变得更加困难。
- 建议在运行时将带有类实例的列表从c++导入qml
- Qt中的实现指针(PIMPL)
- 标准偏差 c++ 公式建议
- 是否有任何建议来统一函数类型限定符并简化可恶的函数类型?
- 有关图像处理应用程序的硬件和软件安全性的建议
- OOP使用不同的结构与孩子一起构建建议
- 意外的 IConnectionPointImpl::不建议调用 Windows Embedded Compact 7
- 在C++中,建议通过数组循环的方式是什么?
- 如何在日食中激活自动建议?
- 提供对不同类型的数据(建议、代码审查)的线程安全访问的类
- 更改 git 分支名称后,在项目的 Visual Studio 代码中丢失智能感知(建议,转到定义C++
- 为什么在PIMPL中无法访问实现类的常量函数?
- 需要有关我的平均计算器代码的建议
- 如何在 c++ 的 toString 方法中使用 setw(x) 并将其放入变量或建议中?
- 我觉得我放入结构中的输入代码可以压缩,关于如何在保持代码简短的同时保持数据个性化的任何建议?
- 当我尝试在函数内声明一个函数时,它不起作用?有什么建议吗?*源代码如下*
- 我无法将数据从数据网格列传输到 C# 中的数组以获取文本框建议收集数据
- 为什么不建议使用宏符号常量定义一个固定长度的数组呢
- 为什么google styleguide建议对大整数默认使用可选的int64_t
- 请求Pimpl框架意见/建议