-3

I am looking for the input from developers who had some experience with IoC frameworks like Castle (in .NET) or in Java.

However, I am looking for something similar implemented using C++. That excludes COM, for reasons I can explain later. Is there a way to implement a pattern like a Factory, Abstract Factory or Service Provider in C++, mostly, but not exclusively on Windows platform?

Specifically, IoC should allow for an unknown number of implementations of interface(s) that can be discovered and loaded by the client app at runtime.

As the answer I am hoping for either:

  1. a link to the specific commercial or open source C++ IoC implementation, or
  2. personal experiences trying to implement it, with the list of a few major gotchas, if any.

Due to unexpected downvotes I have to add some additional specifications (which may have not been clear from the OP):

  1. It is Ok if implementation requires a closed system, where all the components are required to link to the same version of C++ runtime library. This is not great, but is acceptable. I have been working for decade on software that installed the required version of C++ runtime, as the part of client application software installation. IOW, this is an issue for broad compatibility, but not the answer to the question that I am asking here.

  2. I am mentioning unique_ptr because it is a C++ standard. Custom smart pointer would be Ok too. I expect that unique_ptr is probably better explored by now.

  3. I am not asking for a draft code on how to use custom deleter with unique_ptr. Much less for a code that doesn't even compile. This is the answer that got all the upvotes. Sadly, after I commented like "this is like a blind trying to lead a deaf", the rage to punish me as the alleged offender, and to support the alleged victim, got blown out of proportions and resulted in irrational downvotes and upvotes. I am truly saddened that on a site like SO such a thing can derail purely technical problem and turn it into personal rants about politeness. It would be more polite to abstain from throwing clueless code drafts at the questions that clearly ask for actual implementation experiences. (a)

  4. After not getting any sensible answer or a good link, I spent quite some time trying to create a sample implementation myself. I tried it in both Release and Debug builds under MSVC 2010 and it worked as I expected. That is the answer which I didn't vote as accepted, since it is my answer to my own question. I still hope for a good comments from people who are actually experienced in these issues. This answer has 2 downvotes, even if the code does what it says it does, if you actually try to compile and run it.

(a) link to the meaning of the offensive, impolite and rude phrase that caused righteous indignation, according to McGraw-Hill Dictionary of American Idioms and Phrasal Verbs and Cambridge Idioms Dictionary:

Blind leading the Blind

Community
  • 1
  • 1
Tony
  • 1,566
  • 2
  • 15
  • 27
  • possible duplicate of [Is it safe to use STL (TR1) shared\_ptr's between modules (exes and dlls)](http://stackoverflow.com/questions/345003/is-it-safe-to-use-stl-tr1-shared-ptrs-between-modules-exes-and-dlls) – Mgetz Sep 12 '13 at 21:54
  • The question doesn't address shared_ptr but unique_ptr. It is not about is it safe to use it but *how* to use it to implement factory pattern. Safety across dll boundaries doesn't automatically tell you what issues you may face using dynamically loaded, instead of implicitly loaded dlls. I have researched 3 C++ open source factory (in fact IoC) implementations and none have any capability of dealing with components in Dlls. If you understood the question, perhaps you can educate me where are ready-to-use C++ IoC frameworks that, via unique_ptr or else, dynamically load components in DLLs? – Tony Sep 12 '13 at 22:51

2 Answers2

4

If we take a look at the unique_ptr class, we can see by default it uses an instance of the default_delete class. The default_delete class is has the following method: void operator()(T *ptr) const. To implement a custom deleter you would need to create a class looking something like the following (code adapted from 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));
}
BinderNews
  • 570
  • 3
  • 10
  • 2
    Ouch. I wouldn't say I'm blind, deaf, and mute. Possibly deaf, but not all three. It's sorta rude to insult people who are trying to help you. Furthermore, if you have a problem with my code, please be specific. Tell me why it won't/doesn't work not just that it doesn't. – BinderNews Sep 11 '13 at 03:22
  • Okay, how would new IFace work? IFace is an abstract class. You can't instantiate the abstract class. – Tony Sep 11 '13 at 03:34
  • Valid point and my mistake. Fixed. – BinderNews Sep 11 '13 at 03:40
  • 2
    @Tony if you were having cardiac arrest, you wouldn't be here asking for volunteer, free help. You must be polite here. Period. – Andrew Barber Sep 13 '13 at 00:33
  • I think you guys are all extremely exaggerating in your reactions. I said "like blind, etc". It was a metaphor meaning: hey you have even more problems with this than I do. And I said that to the code that doesn't compile and has at least three major flaws. I believe this is a site where code matters, not personal perceptions. Downvoting correct code and upvoting the incorrect one, on SO site, in the name of one personal judgement of what's impolite, that's just flat wrong IMO. – Tony Sep 13 '13 at 00:40
-1

It can be done. Here is a skeleton for sample implementation that I tested. It was built with Visual C++/VS 2010.

Interface is declared in header 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)();

}

Several Dlls implement this interface, while the third Dll called Factory, dynamically loads one of them. In turn, Factory exports a function for use by applications which would link to it implicitly (i.e. via export lib.)

Here is FactoryIface.h, to be included by client apps:

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

Note that custom deleter class must be exported. That was the first thing that I forgot to do. Building client app quickly pointed to it. Below is the crucial part, the implementation of FactoryGetIface() in Factory Dll:

// 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);
}

Note that Dll path is hard-coded: this is just a sample but it compiles and works. In actual application, one could have, for ex. parsing some xml specifying Dll to load.

Very basic console app that uses Iface via Factory goes like this:

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

When I run it I get output like this:

In IfaceImplB constructor
Doing something: Hello from Factory
Doing something: Hello from Factory user
In IfaceImplB destructor
Tony
  • 1,566
  • 2
  • 15
  • 27
  • stl containers cannot be shared across DLLS due to implementation changes, sharing one will cause memory corruption – Mgetz Sep 12 '13 at 21:50
  • There is no problem, for example, using std::string as I use it in interface. It is a working code that you can use directly to build the required components and convince yourself that it works. As I stated in OP "I am also aware of the problems if C++ runtime Dlls, that above-mentioned Dlls will link to, are from different vendors or from the same vendor but different versions". That means that, for the purpose of the question, it can be assumed that I am in complete control of the version of the C++ runtime that would be used. – Tony Sep 12 '13 at 23:42
  • I highly suggest you read the [Microsoft documentation](http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q172396) it's not as simple as simply being on the same version. Each dll has its own heap thus you cannot `delete` an item in one dll that was allocated in another with `new`. This is why [`CoTaskMemAlloc`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms692727(v=vs.85).aspx) exists. So unless you overload `operator new` and `operator delete` globally this won't work. – Mgetz Sep 13 '13 at 00:07
  • I highly suggest that you read some other SO posts on sharing C++ objects across Dll boundaries, not just the one that you quoted as a duplicate. You would read that there is no issue in the code above, as long as all components link to C++ runtime Dll, as oposed to static library linkage. Runtime DLL has *one* heap, shared between all the components in the process. – Tony Sep 13 '13 at 00:11
  • Here is one SO post regarding it: [SO question link](http://stackoverflow.com/questions/7785576/correct-use-of-shared-ptr-to-eliminate-deallocation-across-dll-boundaries) – Tony Sep 13 '13 at 00:17
  • @Mgetz it may surprise you that, if you tried to build that sample from Microsoft documentation that you linked to above, which clearly states that it applies up to VC6, year 2005, with VS 2012, you may find that there is no access violation. That particular issue of static members in STL containers doesn't apply anymore after 7 years when linking (by default) to C++ Dll. Not that it has anything obvious to do with exporting abstract classes across Dll boundaries, just thought I may mention it, hope you won't downvote me again. – Tony Sep 14 '13 at 07:41