Articles Forum Examples Download Open Source
Oct 17, 2007
Copyright(c) 2007 by Pocomatic Software. All Rights Reserved.
PocoCapsule/IoC is an inversion of control (IoC) (a.k.a dependency injection) component framework. It sets up applications based on user provided declarative descriptions. Such descriptions outline intended application structures (including involved components, their instantiation methods, post-instantiation methods, lifecycle control, and dependencies) without imperatively specifying how to construct these structures step-by-step. The framework and its declarative scenario allow application developers and domain users to focus on business logic implementations without concerns of low level plumbing complexities.
Through two examples, this tutorial article illustrates that how plain-old C/C++ object instances, instantiation methods/arguments, post-instantiation operations, lifecycle control attributes, and dependency wirings can be expressed in the core XML declarative schema of PocoCapsule IoC framework. Source code, makefile(s), and README html pages of these two examples, as well as nearly 27 other examples, can all be found in the examples directory of a PocoCapsule installation.
In the first example (examples/basic-ioc/hello), several simplest forms of C++ object manipulations by PocoCapsule framework are presented, starting from the following three:
PocoCapsule is neutral to component programming models. This means that it does not dictate a few predefined classes to be types or parent types of components. Instead, virtually all plain old C/C++ objects (POCOs) are valid components. Examples of these POCOs include instances of arbitrary user defined classes, template classes, K&R structs, unions, arrays, and even function pointers. In this hello example, the type of the to be instantiated object (bean) is a plain old C++ class Foo, declared in the header file foo.h as follows:
|
class Foo {
public:
Foo(const char* msg);
~Foo();
void hello(const char* msg); };
|
This class has a constructor, a destructor and a non-static member function hello(). This Foo class is implemented in the source file foo.C as follows:
|
Foo::Foo(const char* msg)
{
printf(“Hello %s\n”, msg);
}
Foo::~Foo()
{
printf(“That’s all folks! Goodbye!\n”);
}
void Foo::hello(const
char* msg)
{
printf(“Hello %s\n”, msg);
}
|
This source file is compiled and linked into a UNIX shared library or a Windows DLL named as, for instance, foo.so or foo.dll in this example.
Note: Although in this particular example, the component class Foo is declared in a header file, implemented in a separated source file, and then built into a shared library, PocoCapsule/C++ actually does not mandate this partition. With PocoCapsule,
Then, to construct an application from this Foo class, an application description is orchestrated that expresss the following arrangement:
Also, this example assumes the binaries of Foo class implementation and its dynamic invocation proxies are not linked in the runtime environment but to be dynamically loaded as follows:
The XML document (setup.xml) of this application description looks like follows:
|
<?xml version=”1.0”?>
<!DOCTYPE poco-application-context
SYSTEM
"http://www.pocomatic.com/poco-application-context.dtd">
<poco-application-context>
<!-- binaries to be
loaded -->
<load library="./foo.$dll"/>
<load library="./reflx.$dll"/>
<!-- bean
instantiation and lifecycle control methods -->
<bean
class="Foo"
destroy-method="delete"
lazy-init="false">
<method-arg type="cstring" value="Foo
constructor!"/>
<!-- a post
instantiation ioc invocation -->
<ioc method="hello">
<method-arg type="cstring" value="Foo
hello()!"/>
</ioc>
</bean>
</poco-application-context>
|
There is a gap between the component-neutral (i.e. non-invasive) IoC container and container-agnostic plain-old C++ components. In Java IoC containers, this gap is closed using the Java reflection. In C++, there are many ways to skin the same cat. For instance, certain tools (such as gcc-xml and CERN's Reflex) can parse component implementation header files and generate reflection code. Although there are a few insignificant advantages, this approach is more trouble than it's worth. The major problems are a) massive code generation b) bloated runtime footprint, c) easy to be defeated by source code containing non-standard extensions.
PocoCapsule uses an opposite approach. Instead of parsing component header files and generating reflections for all encountered methods regardlessly, PocoCapsule instrument utility pxgenproxy parses the actual application descriptions and only generates dynamic invocation proxies of newly encountered IoC methods. This scenario is a reverse of reflection and therefore is referred to as projection (see further discussion on Reflection from Projection).
Hence, in PocoCapsule, dynamic invocation proxies of the application in this example are generated using the PocoCapsule/C++ instrument utility pxgenproxy from the application description file setup.xml as follows:
|
%
pxgenproxy –h=foo.h setup.xml
|
The generated source code of proxies will be written out to a file that is named as setup_reflx.cc in this example (the "reflx" stands for REFLection-proXies). This file should be compiled and then linked into a shared/dynamic library (e.g. reflx.so or reflx.dll). If there is any error in the application description against the bean class function prototypes, it will be caught and reported during the compiling and linking stages instead of at runtime deployment.
A PocoCapsule/C++ IoC container is a runtime library that is expected to be embedded in user application executables. This example uses such a mini container application executable (main.C) as follows (error handling code has been left-out for simplicity):
|
#include <pocoapp.h>
int main(int argc, char** argv)
{
// parsing the
descriptor
POCO_AppContext* ctxt
=
POCO_AppContext::create("setup.xml", "file");
// all eager singletons will be instantiated.
Their
// post-instantiation ioc methods will be
invoked.
ctxt->initSingletons();
// all instantiated singletons will be
destroyed
ctxt->terminate();
ctxt->destroy();
}
|
This code is compiled and linked with PocoCapsule/C++ runtime (the libpococapsule.so or pococapsule.dll) into an executable file (named as main on Unix/Linux or main.exe on Windows in this example). In executing this application, what will happen are:
Notably, the container and IoC runtime on the upper portion of this diagram do not depend on either the dynamic invocation proxies or application business logic POCO implementations in the lower portion. When proxies are loaded up, they register themselves to the IoC container runtime and will be used in a scenario similar to Java reflection.
Because the mini container of this example is hardcoded to create an application context from the setup.xml, therefore, on starting the above mini container executable, the application described in setup.xml will be set up. To be specifically, on calling the initSingletons() of the application context, the declared eager singleton bean of class Foo will be instantiated. Then, the post-instantiation IoC method hello() will be invoked afterwards. This method prints out the text “Hello Foo hello()!”. On the terminate() of the context, the singleton’s destroy method will be invoked which calls the destructor of the Foo class, that in turn prints out the text “That’s all folks! Goodbye!” These results are shown as follows:
|
% main
Hello Foo
constructor!
Hello Foo hello()!
That’s all folks!
Goodbye!
|
The other two IoC invocation scenarios illustrated in this example declare invocations on the C stdio printf() global function and then, the << operator on std::cout. To do this, a new bean is declared with two post-instantiation <ioc> elements (in the setup.xml).
|
<!xml version=”1.1”?>
...
<poco-application-context>
<load library="./foo.$dll"/>
<load library="./reflx.$dll"/>
<bean
class="Foo"
destroy-method="delete"
depends-on="my-init"
lazy-init="false">
<method-arg
type=”cstring” value="Foo constructor!"/>
<ioc method="hello">
<method-arg type="cstring" value="Foo
hello()!"/>
</ioc>
</bean>
<bean id="my-init"
class="void">
<ioc
method="printf">
<method-arg
type="cstring" value="Hello %s\n"/>
<method-arg
type="cstring" value="C stdio printf()!"/>
</ioc>
<ioc
method="std::cout {{">
<method-arg
type="cstring" value="Hello C++ std::cout!\n"/>
</ioc>
</bean> <poco-application-context>
|
The following details in this new application description are notable:
The two post-instantiation <ioc> elements describe the following operations:
The method attribute of the second <ioc> element is "std::cout {{". This is a user friendly equivalence of the "std::cout <<". PocoCapsule/C++ engine will convert it to "std::cout <<".
Because the application description in setup.xml introduces new kinds of IoC invocations, the proxy library should be regenerated to include proxies for these new signatures. The pxgenproxy instrument utility is used again as follows:
|
%
pxgenproxy -h=foo.h -H=stdio.h -H=stream setup.xml
|
The -h option indicates a user-defined header file, and -H option indicates a standard library or compiler environment defined header.
Like before, the source code of IoC proxies will be written to setup_reflx.cc. Compiling and linking them also into the reflx.so or reflx.dll, then, it is ready to be used by the container.
The mini container built and used previously does not need to be modified and rebuilt, as it does not depend on bean implementation or application description content change.
Running the same mini container executable again, with the new application description in setup.xml, the deployment process will print out the following result:
|
% main
Hello C stdio printf()!
Hello C++ std::cout!
Hello Foo
constructor!
Hello Foo hello()! That's all folks! Goodbye!
|
This example can be found in the examples/basic-ioc/hello directory of a PocoCapsule installation, with more features enabled. For instance, if the main executable started with the command line argument "-Dpococapsule.trace.enable=true", it will print out the process of IoC invocations to stderr as follows (high lighted. The outputs to stdout from the application itself are low lighted):
|
% main
-Dpococapsule.trace.enable=true
printf(...)
Hello C stdio printf()!
std::cout <<(...)
Hello C++ std::cout!
new Foo(...)
Hello Foo constructor!
= (Foo*)0x81ff878
((Foo*)0x81ff878)->hello(...)
Hello
Foo hello()!
delete((Foo*)0x81ff878)
That’s
all folks! Goodbye!
|
The example presented in previous section is trivial and does not involve dependency among beans (except for the life-cycle dependency). The example to be presented next has three components that have dependencies, especially circular dependencies. Source code, Makefile and README.html of this example are located in the examples/basic-ioc/gps directory of a PocoCapsule installation.
This example models a GPS device that comprises of the following three components (as they are illustrated by the diagram in the next page):
The detail interface/operation signatures and implementation of these POCO components are non-critical in comprehending the IoC deployment technique, and therefore are left out, and can be found in the examples/basic-ioc/gps directory of a PocoCapsule installation.
These three components are built into three shared libraries, TickGenImpl.so/dll, GPSlocatorImpl.so/dll and NavDisplayImpl.so/dll, and are all independent of the PocoCapsule/C++ in terms of source code, binaries and runtime.
Although these three components have dependencies, they do not have coupling of one implementation to others or to their factories. The implementation class of any of these components can be changed without affect others, as long as the new class still supports the required interface (e.g. inherits the required super class). The IoC container is now functioning as the object factories. Change implementation class is merely an easy change of the application description.
Note: Although in this particular example, components are implemented in separated source files and built into separate libraries named after their class names, PocoCapsule/C++ does not mandate how component implementations are partitioned into source code and libraries, and how libraries are named. With PocoCapsule,
The application description for this example declares the following configuration of the modeled GPS device:
The declaration above not only declared three bean instances, but also wired them in circular dependency as illustrated in the following diagram:

The XML document (named as setup.xml in this example) of the application descriptionabove looks like follows:
|
<?xml version=”1.0”?>
<!DOCTYPE poco-application-context
SYSTEM
“http://www.pocomatic.com/poco-application-context.dtd”>
<poco-application-context>
<load library="./TickGenImpl.$dll"/>
<bean id="tick-gen"
class="TickGemImpl"
lazy-init="false">
<method-arg type="short" value="10"/>
<method-arg type="short" value="1"/>
<ioc method="subscribe">
<method-arg ref="gps-locator"/>
</ioc>
</bean>
<load library="./GPSLocatorImpl.$dll"/>
<bean id="gps-locator"
class="GPSLocatorImpl"
<ioc method="subscribe">
<method-arg ref="nav-display"/>
</ioc>
</bean>
<load library="./NavDisplayImpl.$dll"/>
<bean id="nav-display"
class="NavDisplayImpl">
<method-arg ref="gps-locator"/>
</bean>
<load library="./reflx.$dll"/>
</poco-application-context>
|
Similarly, the dynamic invocation proxies required by the container reflection engine are generated using the PocoCapsule/C++ instrument utility pxgenproxy from the application description file setup.xml as follows:
|
% pxgenproxy
setup.xml –h=GPSLocatorImpl.h \
-h=NavDisplayImpl.h -h=TickGemImpl.h
|
Similar to the previous example, the source code of IoC proxies will be written to the setup_reflx.cc. Compiling and linking them into the reflx.so or reflx.dll file again, then, it is ready to be used by the container.
The mini container built and used previously in the Hello example can be reused here without any source code change or rebuild, as it is independent of bean implementations or contents of application descriptions .
Running the mini container executable main (or main.exe) again, the application will print out the following results:
|
% main
Generate 10 pulses in
10 seconds
current location is
{1, 199}
current location is
{2, 198}
...
current location is
{10, 190}
|
These two IoC examples are straightforward and even seem to be nothing significant. One would wonder what was the benefits of this technique. Indeed, IoC was thought to be no more than a simple old design pattern/principle, a lousy way of wiring components through dependency injection, or something merely about unit test and "test driven development".
This question is answered in the "Why and What of IoC" and the "Domain Specific Modeling in IoC frameworks". It will become clear that the seamless combination of non-invasive IoC and declarative Domain Specific Modeling (DSM) reveals a software development paradigm shift.
Back to the PocoCapsule article page
This page has been visited:
| free hit counter html code |