スキップしてメイン コンテンツに移動

C++ Dependency Injection (or the next best thing) on Windows

Dependency Injection (DI) is one of the recent buzz phrases within the design pattern community after Martin Fowler picked it up with another related concept Inversion of Control (IoC) in 2004. Since then, it became popular in Java lightweight frameworks where adding late binding was seen as a critical step for upping productivity.

The core value of DI is basically represented in these 2 points:
  1. Testability. DI makes testing easy by enabling loosely coupled components that are easily replaced with mock objects.
  2. Dependency control. When you deploy a DI framework instead of using the Factory Method pattern, dependency can be managed outside of components.
The first point is rather obvious. To understand the second point, I recommend you to browse the Google Giuce lightweight Java DI framework for its video presentation and documents. It illustrates the necessity of such a framework for Java in a straightforward way. Particularly, Giuce appears to be a neat implementation of DI where you don't need to manage XML configuration files unlike other frameworks. Just like inner DSL, dependency is handled in type-safe Java code even though it ditches type-safe factories scattered in code.

But, as Ruby and other dynamic languages gets more attention, the relative mind share of DI seems to get smaller. In Ruby, you can swap methods of instances dynamically at the side of a test class. The dynamic nature of Ruby can enable whole other tricks dependent on it such as ActiveRecord.

So what about DI? Is it just a glorified abstract factory? Unfortunately, there still have to be environments where DI makes things a lot easier. Not just Java, but other static language, such as C++. Since my programming history is MS-Windows-centric, my interest toward DI is its applicability for Windows app development. DI is a lot discussed in the context of Java/.NET enterprise development, but its necessity would be even higher in C++ development on Windows as long as other requirements such as performance permit.



If you are a Windows programmer, IoC can't be a novel concept since without it no Windows GUI application can be built. Plugging in your implementation code into some framework is not an alien concept for us. As for DI, its merit of testability is a big plus for a project that includes many C++ components and consumes mind-bogglingly long time to rebuild.

But DI discussion seems thin around here. Why? It seems existing implementations in Java often rely on reflection which is not available in C++. For example, Giuce uses @inject annotation to tell injection points. It may be just for its AOP features, but lacking reflection makes it a lot harder to implement. Using exotic macros and templates can change the situation, but I guess not much. Another issue is, as I mentioned already, it's a form of late binding hence performance hit is expected. If you design it not to get hit hard, it will not be loosely coupled any more. At this point, I almost gave up the idea of DI for Windows C++.

Instead, my interest shifted to finding the next best thing available which can help us to test binary components without hassle. So I searched - and found a solution. It's called registration-free COM or Reg-Free COM.

OK don't stone me :) It's a very nice feature or a hidden gem in Windows. It's only available on Windows XP and later, but it won't be a problem. The basic idea behind COM (Component Object Model) is a binary interface for reusable components. A part of its concept was later adopted by XPCOM for Mozilla Firefox.

One issue that nags developers and users regarding the use of COM components is it requires Registry entry to resolve an interface query. GUIDs of COM components have to be registered with regsvr32.exe in the HKEY_CLASSES_ROOT (HKCR) Registry key. It makes deployment of COM components really cumbersome even though COM itself is a really reasonable technology. Uninstalling them can be a PITA too.

Enter Reg-Free COM, now Registry entry is not necessary to use a COM component as long as XML manifests are provided to load correct components. In normal COM, the ClassFactory and related platform facility searches Registry entry when a specific CLSID or IID is requested in CoCreateInstance or QueryInterface, whereas in Reg-Free COM dependency resolution is done at application startup by parsing manifests. There are 2 kinds of manifests: application manifest and assembly manifest. The former describes on what components an application depends. The latter describes assembly's identity such as a dll file name and CLSID.

A typical application manifest is as follows (lifted from the sample at MSDN)


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type = "win32"
name = "client"
version = "1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="SideBySide.X"
version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>


It shows it depends on an assembly with the name "SideBySide.X". The dll loader tries to find a file with the name "SideBySide.X.manifest" when reading this application manifest. In SideBySide.X.manifest, its correspondent dll is described.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity
type="win32"
name="SideBySide.X"
version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
clsid="{4B72FC46-C543-4101-80DB-7777848D1357}"
threadingModel = "Apartment" />

<typelib tlbid="{E6A9CD40-8559-4E17-A0D9-C68B038B4FA0}"
version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub
name="ISideBySideClass"
iid="{CBA85B94-9C11-43aa-84F6-30B90145FD3E}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{E6A9CD40-8559-4E17-A0D9-C68B038B4FA0}" />

</assembly>



A comInterfaceExternalProxyStub node is necessary when a component is called in a different kind of a thread apartment from that of the component.

Dependency Injection (DI) is one of the recent buzz phrases within the design pattern community after Martin Fowler picked it up with another related concept Inversion of Control (IoC) in 2004. Since then, it became popular in Java lightweight frameworks where adding late binding was seen as a critical step for upping productivity.


The core value of DI is basically represented in these 2 points:

  1. Testability. DI makes testing easy by enabling loosely coupled components that are easily replaced with mock objects.

  2. Dependency control. When you deploy a DI framework instead of using the Factory Method pattern, dependency can be managed outside of components.


The first point is rather obvious. To understand the second point, I recommend you to browse the Google Giuce lightweight Java DI framework for its video presentation and documents. It illustrates the necessity of such a framework for Java in a straightforward way. Particularly, Giuce appears to be a neat implementation of DI where you don't need to manage XML configuration files unlike other frameworks. Just like inner DSL, dependency is handled in type-safe Java code even though it ditches type-safe factories scattered in code.

But, as Ruby and other dynamic languages gets more attention, the relative mind share of DI seems to get smaller. In Ruby, you can swap methods of instances dynamically at the side of a test class. The dynamic nature of Ruby can enable whole other tricks dependent on it such as ActiveRecord.

So what about DI? Is it just a glorified abstract factory? Unfortunately, there still have to be environments where DI makes things a lot easier. Not just Java, but other static language, such as C++. Since my programming history is MS-Windows-centric, my interest toward DI is its applicability for Windows app development. DI is a lot discussed in the context of Java/.NET enterprise development, but its necessity would be even higher in C++ development on Windows as long as other requirements such as performance permit.



If you are a Windows programmer, IoC can't be a novel concept since without it no Windows GUI application can be built. Plugging in your implementation code into some framework is not an alien concept for us. As for DI, its merit of testability is a big plus for a project that includes many C++ components and consumes mind-bogglingly long time to rebuild.

But DI discussion seems thin around here. Why? It seems existing implementations in Java often rely on reflection which is not available in C++. For example, Giuce uses @inject annotation to tell injection points. It may be just for its AOP features, but lacking reflection makes it a lot harder to implement. Using exotic macros and templates can change the situation, but I guess not much. Another issue is, as I mentioned already, it's a form of late binding hence performance hit is expected. If you design it not to get hit hard, it will not be loosely coupled any more. At this point, I almost gave up the idea of DI for Windows C++.

Instead, my interest shifted to finding the next best thing available which can help us to test binary components without hassle. So I searched - and found a solution. It's called registration-free COM or Reg-Free COM.

OK don't stone me :) It's a very nice feature or a hidden gem in Windows. It's only available on Windows XP and later, but it won't be a problem. The basic idea behind COM (Component Object Model) is a binary interface for reusable components. A part of its concept was later adopted by XPCOM for Mozilla Firefox.

One issue that nags developers and users regarding the use of COM components is it requires Registry entry to resolve an interface query. GUIDs of COM components have to be registered with regsvr32.exe in the HKEY_CLASSES_ROOT (HKCR) Registry key. It makes deployment of COM components really cumbersome even though COM itself is a really reasonable technology. Uninstalling them can be a PITA too.

Enter Reg-Free COM, now Registry entry is not necessary to use a COM component as long as XML manifests are provided to load correct components. In normal COM, the ClassFactory and related platform facility searches Registry entry when a specific CLSID or IID is requested in CoCreateInstance or QueryInterface, whereas in Reg-Free COM dependency resolution is done at application startup by parsing manifests. There are 2 kinds of manifests: application manifest and assembly manifest. The former describes on what components an application depends. The latter describes assembly's identity such as a dll file name and CLSID.

A typical application manifest is as follows (lifted from the sample at MSDN)


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type = "win32"
name = "client"
version = "1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="SideBySide.X"
version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>


It shows it depends on an assembly with the name "SideBySide.X". The dll loader tries to find a file with the name "SideBySide.X.manifest" when reading this application manifest. In SideBySide.X.manifest, its correspondent dll is described.



Back on the topic of DI, the most important point is you can modify dependency by editing these manifests. You can change an application manifest to refer to another dependent manifest. It's possible to change the file name in an assembly manifest to load a different dll that has a different implementation of a coclass with the same CLSID. As long as IID matches you can swap it with a mock object.

Of course there are drawbacks. If you feel making COM objects tedious, it's not for you. But I'd take unit testing without rebuild any day. Also, since it resolves dependency at startup, it can slow down the process a bit if there are too many Reg-Free COM objects. After startup the overhead should not be larger than regular COM usage.

One thing I'd like to emphasize in this article is reinventing the wheel costs you a lot. Instead, first consider using what's available to your liking before jumping the gun to write your own version. Most likely, someone already came up with a decent idea deliberately or indeliberately.

コメント