使用unique_ptr跨越Dll边界的工厂模式

时间:2013-09-11 01:40:45

标签: windows dll c++11 inversion-of-control unique-ptr

我正在寻找那些对使用Castle(.NET)或Java等IoC框架有一定经验的开发人员提供的意见。

但是,我正在寻找使用C ++实现的类似功能。这不包括COM,原因我稍后可以解释。有没有办法在C ++中实现像Factory,Abstract Factory或Service Provider这样的模式,主要是但不仅限于Windows平台?

具体来说,IoC应该允许在运行时由客户端应用程序发现和加载的未知数量的接口实现。

作为我希望的答案:

  1. 指向特定商业或开源C ++ IoC实施的链接,或
  2. 试图实施它的个人经历,以及一些主要问题的清单,如果有的话。
  3. 由于意外的downvotes,我必须添加一些额外的规范(OP可能尚未明确):

    1. 如果实现需要一个封闭的系统,则需要将所有组件链接到相同版本的C ++运行时库。这不是很好,但可以接受。作为客户端应用程序软件安装的一部分,我已经在安装了所需版本的C ++运行时的软件上工作了十年。 IOW,这是广泛兼容性的问题,但不是我在这里问的问题的答案。

    2. 我提到unique_ptr因为它是C ++标准。自定义智能指针也可以。我希望现在可以更好地探索unique_ptr。

    3. 我不是要求有关如何使用unique_ptr使用自定义删除器的草稿代码。对于甚至不编译的代码来说要少得多。这是获得所有赞成的答案。可悲的是,在我评论说“这就像是一个盲目的试图聋子”之后,愤怒的惩罚我作为被指控的罪犯,并支持所谓的受害者,被吹得不成比例,导致了非理性的downvotes和upvotes。我真的很难过,在像SO这样的网站上,这样的事情可能会破坏纯粹的技术问题并将其转化为关于礼貌的个人咆哮。在明确要求实际实施经验的问题上放弃投掷无能的代码草案会更有礼貌。 (a)中

    4. 在没有得到任何明智的答案或良好的链接之后,我花了很多时间尝试自己创建一个示例实现。我在MSVC 2010下的Release和Debug版本中尝试了它,它按照我的预期工作。这是我没有接受投票的答案,因为这是我对自己问题的回答。我仍然希望得到那些在这些问题上真正经验丰富的人的好评。这个答案有2个downvotes,即使代码完成了它所说的,如果你真的尝试编译并运行它。

    5. 根据McGraw-Hill美国成语词典和短语动词和剑桥成语词典,

      (a)链接到引起正义愤慨的令人反感,无礼和无礼的含义:

      Blind leading the Blind

2 个答案:

答案 0 :(得分:3)

如果我们看一下unique_ptr类,我们可以看到它默认使用default_delete类的实例。 default_delete类具有以下方法: void operator()(T *ptr) const。要实现自定义删除器,您需要创建一个类似于以下内容的类(代码改编自here):

class MyDeleter
{
public:
    MyDeleter(FactoryReference *f)
    {
        m_factoryRef = f;
    }
    void operator()(IFace *ptr) const
    {
        delete ptr;
        m_factoryRef->unloadDLL();
    }
private:
    FactoryReference *m_factoryRef;
};

std::unique_ptr<IFace, MyDeleter> GetIFace()
{
    FactoryReferece *myFactory = /* a factory reference */;
    return new std::unique_ptr<IFace, MyDeleter>(myFactory->getIFaceSubclass(), MyDeleter(myFactory));
}

答案 1 :(得分:-1)

可以做到。这是我测试的示例实现的框架。它是使用Visual C ++ / VS 2010构建的。

接口在头文件Iface.h中声明:

#pragma once

#include <memory>
#include <string>

#ifdef IFACE_EXPORTS
#define IFACE_API __declspec(dllexport)
#else
#define IFACE_API __declspec(dllimport)
#endif

namespace Generic
{

class Iface
{
public:
   virtual void DoSomething(std::string const&) = 0; 
   virtual ~Iface() {};
};

IFACE_API std::unique_ptr<Iface> GetIface();

typedef std::unique_ptr<Iface> (*GetIfaceType)();

}

多个Dll实现此接口,而第三个Dll称为Factory,动态加载其中一个。反过来,Factory会导出一个供应用程序使用的函数,这些函数会隐式链接到它(即通过export lib。)

这是FactoryIface.h,由客户端应用程序包含:

#pragma once

#include <memory>
#include "Iface.h"

#ifdef FACTORY_EXPORTS
#define FACTORY_API __declspec(dllexport)
#else
#define FACTORY_API __declspec(dllimport)
#endif

class FACTORY_API IFaceCustomDeleter
{
   void* hMod_;
public:
   IFaceCustomDeleter(void* hMod=nullptr);
   void operator()(Generic::Iface* ptr);
};

FACTORY_API std::unique_ptr<Generic::Iface, IFaceCustomDeleter> FactoryGetIface();

请注意,必须导出自定义删除器类。这是我忘记做的第一件事。构建客户端应用程序快速指向它。以下是关键部分,Factory Dll中FactoryGetIface()的实现:

// A trick to get decorated name of exported function
std::string DecoratedName;

namespace Generic
{
   std::unique_ptr<Iface> GetIface()
   {
      DecoratedName = __FUNCDNAME__;
      return std::unique_ptr<Iface>(nullptr);
   }
}

using namespace Generic;

// for keeping Iface from dynamically loaded DLL
std::unique_ptr<Iface> ExternalIface;

// Custome deleter implementation

IFaceCustomDeleter::IFaceCustomDeleter(void* hMod) 
   : hMod_(hMod) 
{
}

void IFaceCustomDeleter::operator()(Iface* ptr)
{
   ExternalIface.reset(nullptr);
   if(hMod_)
      ::FreeLibrary((HMODULE)hMod_);
}


FACTORY_API std::unique_ptr<Generic::Iface, IFaceCustomDeleter> FactoryGetIface()
{
   // determine path of DLL to load for Iface implementation
   auto path = L".\\IfaceImplB.dll";

   auto hmod = ::LoadLibrary(path);
   if(hmod)
   {
      GetIface(); // get decorated name

      GetIfaceType ptrGetIface;
      ptrGetIface = (GetIfaceType)::GetProcAddress(hmod, DecoratedName.c_str());
      // Alternatively, use full decorated name like below:
      //ptrGetIface = (GetIfaceType)::GetProcAddress(hmod,
      //"?GetIface@Generic@@YA?AV?$unique_ptr@VIface@Generic@@U?$default_delete@VIface@Generic@@@std@@@std@@XZ");

      if(ptrGetIface)
      {
         ExternalIface = ptrGetIface();
         ExternalIface->DoSomething("Hello from Factory");
         IFaceCustomDeleter del(hmod);
         return std::unique_ptr<Generic::Iface, IFaceCustomDeleter>(ExternalIface.get(), del);
      }
   }

   IFaceCustomDeleter del;
   return std::unique_ptr<Generic::Iface, IFaceCustomDeleter>(nullptr, del);
}

请注意,Dll路径是硬编码的:这只是一个示例,但它可以编译并运行。在实际应用中,人们可以拥有,例如。解析一些指定要加载的Dll的xml。

使用Iface via Factory的非常基本的控制台应用程序如下:

#include "FactoryIface.h"

int _tmain(int argc, _TCHAR* argv[])
{
   {
      std::unique_ptr<Generic::Iface, IFaceCustomDeleter> iface = FactoryGetIface();
      iface->DoSomething("Hello from Factory user");
   }

    return 0;
}

当我运行它时,我得到这样的输出:

In IfaceImplB constructor
Doing something: Hello from Factory
Doing something: Hello from Factory user
In IfaceImplB destructor