请求 Pimpl 框架意见/建议

发布于 2024-11-02 02:04:01 字数 11453 浏览 1 评论 0原文

我基本上已经实施了一个提案,我的问题是,它已经完成了吗?如果完成了,在哪里?和/或有更好的方法来做我正在做的事情吗?抱歉这篇文章的长度,除了提供代码之外,我不知道有更好的方法来解释我的方法。

我之前问过这个问题 pimpl:避免使用 pimpl 指向指针?

为了在这里再次解释这个问题,基本上,假设我们有一个接口 interface 和一个实现 impl。此外,就像 pimpl 习惯用法一样,我们希望能够单独编译 impl

现在,在 c++0x 中执行此操作的一种方法是在 interface 中创建一个指向 implunique_ptrinterface 的方法的实际实现并不包含在 main.cpp 中,它们是在 interface.cpp 中单独编译的,以及impl 的接口和实现。

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

但后来我在想如果我真的想要一个指向这个 pimpl 的共享指针怎么办?我可以只做shared_ptr,但随后我会有一个shared_ptr到一个unique_ptr,我认为这有点愚蠢。我可以在interface内使用shared_ptr而不是unique_ptr,但它仍然会使用.表示法调用函数,并且看起来并不像指针,因此当它进行浅复制时可能会让用户感到惊讶。

我开始认为,最好有一些通用模板类来连接接口 X 和任何兼容的 X 对的相应实现 Y > 和 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.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 的 code> 实际上返回一个真正的 shared_ptr,没有双重重定向。 get_impl() 中的 static_cast 是一团糟,遗憾的是,除了在继承树上向上两步,然后向下一步到实现之外,我不知道更好的方法。

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

您可以在此处下载 zip 中的所有文件。他们将解压到当前目录。您需要使用具有某些 c++0x 功能的东西进行编译,gcc 4.4.5 和 gcc 4.6.0 对我来说都工作得很好。

所以就像我说的,任何建议/评论将不胜感激,如果这已经完成(可能比我更好),如果你能指导我,那就太好了。

I've basically implemented a proposal, my question is, has it been done, and if so, where? And/or is there a better way to do what I'm doing? Sorry about the length of this post, I didn't know a better way to explain my approach other than providing the code.

I previously asked the question pimpl: Avoiding pointer to pointer with pimpl?

To explain that question again here, basically, lets say we've got an interface interface and an implementation impl. Further, like the pimpl idiom, we want to be able to separately compile the impl.

Now a way to do this in c++0x is to make a say unique_ptr in the interface which points to the impl. The actual implementation the methods of interface are not included in main.cpp, they are compiled separately in say interface.cpp, along with both the interface and implementation of impl.

We design this class as if the pointer is not there so it's effectively transparent to the user. We use the . notation to call methods, not the -> notation, and if we want to copy, we implement deep copy schematics.

But then I was thinking what if I actually wanted a shared pointer to this pimpl. I could just do shared_ptr<interface>, but then I'd have a shared_ptr to a unique_ptr, and I thought that was a bit silly. I could use shared_ptr instead of unique_ptr inside interface, but then it would still use the . notation calling functions, and wouldn't really look like a pointer, so might surprise users when it shallow copies.

I began to think it would be good to have some generic template class that connected an interface X and a corresponding implementation Y for any compatible pair of X and Y, handling a lot of the pimpl boilerplate stuff.

So below is how I've attempted to do it.

First I'll start with 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);
}

Basically here, x1 is the standard unique_ptr pimpl implementation. x2 is actually a shared_ptr, without the double pointer caused by a unique_ptr. A lot of the assignments and constructors were for testing only.

Now 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 just contains some boilerplate code which is required for the framework I've developed. The interface takes HANDLER as a template argument and makes it a base class, this constructors just ensure things are forwarded to the base HANDLER where the action happens. Of course interface itself will have no members and no constructors for it's on purpose, just some public member functions.

Now interface.cpp is our other file. It actually contains implementation of interface, and despite its name, also the interface and implementation of impl. I won't list the file in full yet, but the first think it includes is interface_impl.hpp (sorry about the confusing naming).

Here is 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

Note the get_impl() method call. This is going to be provided by HANDLER later.

impl.hpp contains both the interface and implementation of impl. I could have separated these, but didn't see a need. Here is 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

Now lets have a look at unique_pimpl.hpp. Remember this was included in main.cpp, so our main program has a definition of this.

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

Here we will pass the template class INTERFACE (which has one parameter, HANDLER, which we will fill in here with unique_pimpl), and the IMPL class (which is in our case impl). This class is where the unique_ptr actually resides.

Now this here provides the get_impl() function that we were looking for. Our interface can call this function so it can forward calls to the implementation.

Lets have a look at 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

Now a lot of the above is just boiler plate code, and does what you expect. create(...) simply forwards to the constructor of impl, which otherwise wouldn't be visible to the user. Also there is a macro definition DEFINE_UNIQUE_PIMPL which we can use later on to instantiate the appropriate templates.

Now we can come back to 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();
  }
}

This makes sure all the appropriate templates are compiled instantate_my_unique_pimpl_create_functions() ensures we compile a 0-argument create and is otherwise never intended to be called. If impl had other constructors we wanted to call from main, we could define them here (e.g. my_unique_pimpl::create(int(0))).

Looking back up at main.cpp, you can now see how unique_pimpls can be created. But we can create other joining methods, and here is 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

Note that create for shared_pimpl actually returns a real shared_ptr, without a double redirection. The static_cast in get_impl() are a mess, sadly I didn't know a better way to do it other than go two steps up the inheritance tree and then one down to the implementation.

I can imagine making other "HANDLER" classes for intrusive pointers for example, and even a simple stack allocated join which requires all header files to be included in the traditional way. That way users can write classes that are pimpl ready but not pimpl required.

You can download all the files from a zip here. They will extract to the current directory. You'll need to compile with something with some c++0x features, both gcc 4.4.5 and gcc 4.6.0 worked fine for me.

So like I said, any suggestions/comments would be appreciated, and if this has been done (probably better than I have) if you could direct me to it that would be great.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

醉酒的小男人 2024-11-09 02:04:01

它,真的,对我来说似乎非常复杂......

您建议的 . 语义需要定义“接口”两次:

  • 一次用于您的 Proxy
  • 一次用于基类

它是直接违反了DRY,却收获甚微!

我认为使用您的类没有太多意义,在共享所有权的情况下,只需使用 std::shared_ptr 即可。

我自己写了一个pimpl实现模板有一个原因,就是为了适配shared_ptrdeleter实现+深度复制语义,从而获得不完整类型的值语义。

在代码中添加多层帮助程序最终会使浏览变得更加困难。

It, really, seems awfully complicated to me...

The . semantics you propose require to define the "interface" twice:

  • Once for your Proxy
  • Once for the base class

It's a direct violation of DRY, for so little gain!

I don't see much point in using your class over using, simply std::shared_ptr in case of a shared ownership.

There is one reason I myself wrote a pimpl implementation template, and this was for adapting the shared_ptr deleter implementation + deep copying semantics, so as to get value semantics for incomplete types.

Adding layers of helpers in the code ends up making it more difficult to browse.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文