
The article tells about the experience of acquaintance with the PocoCapsule IoC container (C ++), the difficulties encountered and how to overcome them. Among other things, the article includes a small example for a quick start with PocoCapsule (simplified project
“Hello World” from the official site ).
In my humble opinion, IoC technology is the most important component of any more or less significant project, therefore, the correct use of the IoC container is crucial. Therefore, after becoming acquainted with PocoCapsule, I decided to write this article. Not just to share my experience, but also to make sure that I did everything correctly and understood everything correctly, because, as you know, the best way to understand something is to try to explain it to another person.
')
In order to run the “Hello world” application using PocoCapsule, it took me about 6 hours. And, periodically, I wanted to send everything to hell, because hands fall when you see the message “failure: null”, although you do everything according to the textbook. Given that a similar start in Spring IoC takes about 30-60 minutes on the strength.
PocoCapsule is an
IoC container that works with standard C ++ objects (POCO) and makes it quite tricky. PocoCapsule cannot, after reading the configuration xml file, directly create the required POCO objects, since in C ++ there is no reflection mechanism.
For example, there is a class Foo:
class Foo {
public :
Foo() {};
~Foo() {};
}
* This source code was highlighted with Source Code Highlighter .
and container xml configuration:
<poco-application-context>
<bean class = "Foo"
destroy-method= "delete"
lazy-init= "false" >
</bean>
</poco-application-context>
* This source code was highlighted with Source Code Highlighter .
After reading the xml configuration, PocoCapsule should create an object of class Foo. But how to do that? After all, PocoCapsule does not depend on your source code and cannot know what kind of class this Foo is and how to handle it, so naturally, it cannot directly create an object of this class.
To solve this problem, PocoCapsule generates proxy methods for each class of yours - methods that deal directly with your classes (create, delete, etc.). So, to call the constructor Foo, a method will be generated:
// ---------------------------------------
// Proxies of constructors and factories
// ---------------------------------------
//
// new Foo()
//
static void * _poco_proxy_0( void * _poco_this, void * _poco_params[])
{
Foo* _poco_var_retv;
_poco_var_retv = new Foo();
return ( void *)_poco_var_retv;
}
* This source code was highlighted with Source Code Highlighter .
To remove, respectively:
// -------------------------------------
// Proxies of dup and destroy methods
// -------------------------------------
//
// delete(Foo*)
//
static void * _poco_proxy_1( void * _poco_this, void * _poco_params[])
{
int _poco_i = 0;
Foo* _poco_var_0 = (Foo*)(_poco_params[_poco_i++]);
delete(
_poco_var_0);
return ( void *)0UL;
}
* This source code was highlighted with Source Code Highlighter .
Then the methods for registering these “poco_proxy” in the container are also generated. In the end, these proxy methods are combined in a common source file that connects to the project and is the link between your classes and PocoCapsule.
Problems with the original “Hello word”. (Ubuntu 10.04 LTS environment)
I will describe in order the problems that I encountered when meeting PocoCapsule and which took so much of my time.
- After unpacking the binary, the proxy generator does not start (bin / pxgenproxy). We have the following:
caiiiycuk @ caiiiycuk-laptop: ~ / pococapsule-cpp / bin $ ./pxgenproxy
./pxgenproxy: undefined symbol: JNI_CreateJavaVM
At the same time, I have both JRE and JDK installed (pocoapsule uses Java to generate proxies and not only).
To solve this problem, you need to set the environment variable POCOCAPSULE_DIR = (folder with unpacked PocoCapsule). It is very old that this requirement is not described anywhere in the documentation. It is supposed that it should work out of the box, but it did not work for me.
The correct algorithm is:
caiiiycuk@caiiiycuk-laptop:~/pococapsule-cpp/bin$ export POCOCAPSULE_DIR=/home/caiiiycuk/pococapsule-cpp/
caiiiycuk@caiiiycuk-laptop:~/pococapsule-cpp/bin$ ./pxgenproxy
------------------------------------------------------------
Pocomatic Software, Dynamic Proxy Generator, version 1.0
------------------------------------------------------------
Usage:
./pxgenproxy (xml-file|option)*
options:
-help : print this page
-s=[name] : suffix and extend name of proxy file,
default value is '_reflx.cc'
-H=[std-header-file] : use the specified standard header file
-h=[usr-header-file] : use the specified user defined header file
-r=[gather|scatte] : recursively step into imported resources and gather or scatte
proxy generation result into one or multiple individual proxy file(s)
caiiiycuk@caiiiycuk-laptop:~/pococapsule-cpp/bin$
* This source code was highlighted with Source Code Highlighter .
- When you try to raise the context, nothing happens - we get a cheerful null instead of the context. Having done all the tutorial and running the compiled main.C, we get "failure: null", because cxtx == null:
/****************************************************************************/
/* */
/* Copyright 2006, by Pocomatic Software, LLC. All Rights Reserved. */
/* */
/****************************************************************************/
#include "pocoapp.h"
#include <stdio.h>
#include < string .h>
int main( int argc, char ** argv)
{
POCO_AppEnv* env = POCO_AppContext::initDefaultAppEnv(argc, argv);
POCO_AppContext* ctxt = POCO_AppContext::create( "setup.xml" , "file" );
if ( ctxt == NULL || !ctxt->initSingletons() ) {
printf( "failure: %s\n" , env->get_message());
return -1;
}
ctxt->terminate();
ctxt->destroy();
return 0;
}
* This source code was highlighted with Source Code Highlighter .
And the reason is the same “POCOCAPSULE_DIR”: if you declare this environment variable before launching, everything starts working. This is due to the fact that PocoCapsule uses JAXP by default to parse the xml files. Sad but true :( As you understand, dragging Java with you to parse xml files is not very pleasant ... Fortunately, there are two ways to fight it off: use a native xml-reader based on Xerces-C ++ or use encoded xml descriptors. Unfortunately, I went the first way and it only added problems. I recommend everyone to follow the second way.
- PocoCapsule native xml-reader (libpocoxml.so) is compiled for Xerces-C ++ version 2.7, which has already gone out of fashion and will not be available anywhere. To solve this problem, download the pococapsule source, install xerces-c (dev) into the system, go to the daddy pococapsule-cpp-1.1-src / src / xmlreader and execute make. After successful compilation, we take the valid libpocoxml.so from the lib daddy.
- PocoCapsule ignores our desire to use Xerces-C ++ (libpocoxml.so) and still requires JAXP (libpocoxsl.so). The tutorial tells us that if we connect libpocoxml.so to our project, then PocoCapsule will automatically use it instead of libpocoxsl.so. Yes it is - on my “Hello word” project, it was done. But in another (more complex) project, in which other libraries are also connected, the connection of libpocoxml.so does not give effect - the project persistently requires libpocoxsl.so. After unsuccessful dances with a tambourine, I stupidly renamed libpocoxml.so to libpocoxsl.so and it all worked.
Among other things, the “Hello World” project for a textbook implies dynamic linking. I believe that it can be done much easier for the needs of dating something.
The simplest "Hello World"For development, I use Ubuntu 10.04 LTS and
Netbeans 6.8 , as well as PocoCapsule 1.0 binaries.
- Create a new C ++ project. We include header files (Project properties-> Build-> ++ ompiler-> Include directories: pococapsule_dir / include). We specify the linker where to search for libpococapsule.so (Project properties-> Build-> Linker-> Libraries: pococapsule_dir / lib / libpococapsule.so).
- Create a class Foo (Foo.h):
#ifndef _FOO_H
#define _FOO_H
#include <iostream>
class Foo {
public :
Foo() {
std::cout << "Hello world!" << std::endl;
}
};
#endif /* _FOO_H */
* This source code was highlighted with Source Code Highlighter .
- Create an xml context (context.xml):
<?xml version= "1.0" encoding= "iso-8859-1" ?>
<!DOCTYPE poco-application-context
SYSTEM "http://www.pocomatic.com/poco-application-context.dtd" >
<poco-application-context>
<bean class = "Foo"
destroy-method= "delete"
lazy-init= "false" >
</bean>
</poco-application-context>
* This source code was highlighted with Source Code Highlighter .
- Generate proxy methods for our class Foo. Open the console in the project directory and execute:
export POCOCAPSULE_DIR=( PocoCapsule)
$POCOCAPSULE_DIR/bin/pxgenproxy -h=Foo.h context.xml
* This source code was highlighted with Source Code Highlighter .
After successful generation, the context_reflx.cc file appears, we will add this file to the project in order for NetBeans to build it (Sources Files-> Add Exsisting Item). Check that everything builds successfully.
- Encode the xml context to get rid of JAXP. Open the console in the project directory and execute:
export POCOCAPSULE_DIR=( PocoCapsule)
$POCOCAPSULE_DIR/bin/pxencode context.xml
* This source code was highlighted with Source Code Highlighter .
At the output, we get context_poco.ctx.
- Edit main.cpp:
#include "pocoapp.h"
#include <stdio.h>
#include < string .h>
int main( int argc, char ** argv)
{
POCO_AppEnv* env = POCO_AppContext::initDefaultAppEnv(argc, argv);
POCO_AppContext* ctxt = POCO_AppContext::create( "context_poco.ctx" , "file" );
if ( ctxt == NULL || !ctxt->initSingletons() ) {
printf( "failure: %s\n" , env->get_message());
return -1;
}
ctxt->terminate();
ctxt->destroy();
return 0;
}
* This source code was highlighted with Source Code Highlighter .
Running see "Hello World!"
That's all :)
Thanks for attention.