2010 May 25 - Tue
C++ Curiously Recurring Template Pattern
Through the years of maturing in software development, I have migrated through a
series of technologies to solve various programming problems.
During the initial stages of my C++ usage, I used the tried and true run-time dynamic
polymorphism, mostly known as virtual methods through class inheritance. To answer the question of
when to use virtual destructors, Herb Sutter has an excellent article called
Virtuality.
For more virtual destructor information, Item 33 in Scott Meyer's More Effective C++ is helpful.
Inheritance - virtual Functions FAQ has more useful information.
Class inheritance and virtual functions closely couple classes. I wanted start some
decoupling, and do some event based coupling through C#-like events/delegates.
C++ doesn't have a similar concept built-in, but there are various libraries available
while supply a similar concept: Boost's slot/signal system, or the one I ended up using:
FastDelegates.
FastDelegates are supposed to be fast. And they do work well.
As I do some work on the Windows platform, I had been using the MFC classes for some GUI work.
As I got more into multi-threaded designs, MFC started to show it's significant short-comings
related to modular and mult-threaded designs. I came across the Windows Template Library (WTL)
as a nice, fast, light-weight windowing library.
The WTL introduced to me the concept of the Curiously Recurring Template Pattern (CRTP). WTL and
ATL make significant use of the CRTP pattern. A good introduction can be found at Wikipedia's entry for
Curiously Recurring Template Pattern.
CRTP has brought me back tight-coupling of classes, but as a consequence, it offers up the ability to
integrate a number of concepts together: slot/signals aka delegates, maintenance of strong typing,
simulated dynamic binding aka static polymorphism, and fast execution.
The close-at-hand references don't mention one other refinement (I wish I could find the
original source of this trick), and that is one of conditional static polymorphism. There is a way
to conditionally make the polymorphism call: if the derived class doesn't over-ride a method,
the calling code doesn't get compiled, it gets optimized away.
For example:
template <typename T>
class base {
void implementation( void ) {};
void interface( void ) {
if ( &base<T>::implementation != &T::implementation ) {
static_cast<T*>( this )->implementation();
}
}
};
class derived1: public base<derived> {
};
class derived2: public base<derived> {
void implementation( void ) {};
};
In this example of the CRTP, the base class has a default implementation of the interface.
In class derived1, as there is no implementation defined, no implementation gets called. In
class derived2, where there is an implementation defined, it is called.
[/Personal/SoftwareDevelopment]
permanent link
2010 Apr 02 - Fri
Checked Iterators in Microsoft Visual C++
I've wondered why some of my C++ programs seem to run slower than I think they should be
running. Then I came across an article regarding Checked Iterators, and it become clearer
to me where some of my execution speed issues could be. By default, in debug mode,
all standard iterators are bounds checked. This checking can cause a slow down in
execution speed when using standard iterators extensively.
The solution is to set certain macros to selectively enable and disable the checking
on proven code. A
Microsoft MSDN article on Checked Iterators
describes the two symbols used for controlling the checked iterators feature:
- _SECURE_SCL: If defined as 1, unsafe iterator use causes a runtime error. If defined as 0, checked iterators are disabled. The exact behavior of the runtime error depends on the value of _SECURE_SCL_THROWS. The default value for _SECURE_SCL is 1, meaning checked iterators are enabled by default.
- _SECURE_SCL_THROWS: If defined as 1, an out of range iterator use causes an exception at runtime. If defined as 0, the program is terminated by calling invalid_parameter. The default value for _SECURE_SCL_THROWS is 0, meaning the program will be terminated by default. Requires _SECURE_SCL to also be defined.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Oct 17 - Sat
Memory Leak Detection in MSVC 2008 C++
In Visual Studio, when building debug releases, I seem to recall that
memory leak detection was automatically enabled. In Visual Studio 2008, memory leak
detection is not automatically enabled. Code will need to be added to the source
files to make it available.
Memory Leak Detection Enabling
is a document in MSDN describing how to enable the ability. Basically, to enable the debug heap
functions, include the following statements:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
Immediately before the program exits, include the following function call:
_CrtDumpMemoryLeaks();
One commenter indicates that this only works best with C code, ie, C code will
get discriptive comments, but C++ code will get cryptic memory reports.
In order to get the file and line number to work you need to manually redefine new in your code.
This is done by undefining new, and redefining it to point to the debug versions that take a file and line number.
During debugging, in Windows based applications, std::cout no longer sends text into the
IDE's Output window. Instead, the function OutputDebugString( "..." ) needs to be used.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Oct 01 - Thu
Building Boost 1.40.0 on Debian Linux
Boost builds well on Linux. To get a clean build, I needed two libraries.
With Python already installed, I needed to 'apt-get install python-dev'.
The iostreams library needed the bzip2 libraries which can be installed through
'apt-get install libbz2-dev'.
After downloading bjam from sourcforge, my build then used:
bjam install --toolset=gcc --prefix=/usr/local --layout=tagged variant=debug threading=multi link=static
Instead of 'debug', 'release' can be used.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Aug 19 - Wed
Boost Bind/Lambda replaced by Boost Spirit/Phoenix
Regular users of the C++ library known as Boost will already know about
functors, lambda functions, and the like. These abilities mostly originate in
the Boost.Bind and Boost.Lambda libraries.
As I'll soon be using the functor capability within my C++ programs, I wanted to
make a 'note-to-self' regarding the fact that Boost.Bind and Boost.Lambda have basically
been superceded by Boost.Spirit.Phoenix.
The Phoenix library has been accepted into Boost based upon
Hartmut's summary of the Phoenix review.
The current incarnation of the
Boost libraries is 1.39. Here is the
Spirit User's Guide which includes a link to the
Phoenix Documentation and a link to the
Phoenix Users Guide.
It is noted that FC++ influenced Phoenix, and when looking at the
FC++ web site, there
is a reference to
LC++, which is a Logic Programming
language built atop of FC++. I wonder if something similar has been done atop of Phoenix.
An example from the Boost Mailing List of using Phoenix:
#include <vector>
#include <algorithm>
#include <boost/shared_ptr.hpp>
#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
struct A {};
void foo( const A& ) {}
int main()
{
using namespace boost::phoenix;
using namespace boost::phoenix::arg_names;
std::vector< boost::shared_ptr< A > > vec;
vec.push_back( boost::shared_ptr< A >( new A ) );
std::for_each( vec.begin(), vec.end(),
bind( &foo, *arg1 ) );
return 0;
}
Colorized with CodeColorizer.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Aug 18 - Tue
Model View Controller
In the world of software development, programmers will make use of 'patterns' as a form of programming template.
By defining and segregating certain forms of functionality, code reuse and modularity can be enhanced. One regularily
recurring form of functionality involves some form of data collection (a model), some form of view of the data (a view), and
some form of interaction with the view and data (a controller). This collection has been defined as a
the Model-View-Controller (MVC) pattern.
The best diagram I've found which depicts the relationships between these three sets of functions can be
found at
Designing Enterprise Applicationswith the J2EETM Platform, Second Edition. I've included the diagram here:
[/Personal/SoftwareDevelopment]
permanent link
2009 Aug 02 - Sun
Building HDF5 in Microsoft Visual Studio 2008
A while ago, I wrote an article about
HDF Group's Hierarchical Data Format (HDF5) Library . In the article, there were some brief installation
instructions. This article adds some refinements to the installation instructions.
You need to start by downloading the compression libraries:
Create a sub-directory called 'compress' somewhere. In that sub-directory, create two additional sub-directories:
'include' and 'lib'.
Unzip the two downloads. From each of the two uncompressed libraries, put all the .lib and .dll files into the
sub-directory .\compress\lib, and put all the .h files into the .\compress\include sub-directory.
In Windows, create two environment variables by going: Start->ControlPanel->System->Advanced->EnvironmentVariables,
and then create two new user variables:
- HDF5_EXT_SZIP = szlibdll.lib
- HDF5_EXT_ZLIB = zlib1.lib
The remaining build instructions focus on building the useful HDF5 C++ libraries for HDF5 v1.9, with v1.9.43
being the latest as of this writing. Download
/hdf5-1.9.43.tar.gz and expand it with
7-Zip into a working sub-directory called
hdf5-1.9.43. Run .\hdf5-1.9.43\windows\copy_hdf.bat. Double click on
.\hdf5-1.9.43\windows\proj\all\all.sln to open the Visual Studio Solution. The file is in version 2005. VS 2008
will ask to convert it for you. You'll need to do so.
After the conversion, go into Tools->Options->ProjectsAndSolutions->VC++Directories.
Set 'Include Files' to the full path of your .\compress\include sub-directory, and set
'Library Files' to the full path of your .\compress\lib sub-directory.
For the project properties, choose whether you are doing a debug build or a release build. Do the build.
For v1.9.43, I found that there was one debug executable that wouldn't build, which, since I'm only interested in
some key libraries, has no effect on my required outcome.
After the build process is complete, open a command prompt in .\hdf5-1.9.43, and run 'installhdf5lib.bat'. The various
.dll, .lib, and .h files will be in \dll, \lib, and \include off of .\hdf5-1.9.43\hdf5lib.
From Sysinternals, download
junction.exe. This
allows you to create symbolic links between directories. Put the program somewhere in your path. Then use it
to create a symbolic link from your existing project to the hdf5lib directory. This will allow you
to change library versions with a simple symbolic link change. For example, something like the following
will set a link to the include files where ever you installed and built the hdf5 libraries.
- junction hdf5 .\hdf5-1.9.43\hdf5lib
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Jul 29 - Wed
A Singleton Per Thread
A while ago, I had written about
singletons, and how there isn't something straight-forward in Boost. Recently,
I've seen references to a couple of interesting messages regarding not only singletons, but how to get a singleton per thread.
One starts by considering
Boost Thread Local Storage and how to use it.
Then one can consider the concept of a
thread-safe lazy singleton template class from the Boost Cookbook, which
a singleton implementation not referenced in my other article.
Rutger ter Borg suggested the following untested possible code snippet:
template< typename Singleton >
Singleton& get_singleton() {
static boost::thread_specific_ptr< Singleton > m_singleton;
if ( !m_singleton.get() ) {
m_singleton.reset( new Singleton() );
}
return *m_singleton;
}
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 May 03 - Sun
64 Bit Data Models
As we move to 64 bit processors, variable types and their widths change. I had
originally thought that there would be a consistent naming convention as one moved from 32
bit programming to 64 bit programming. At a
64 Bit
Wiki Entry, I find that such is not the case. Different compilers choose different
ways. For example the Microsoft VC compiler will use the LLP64 model which keeps an int
as 32 bits. This is something that one needs to keep in mind when re-compiling software
created for 32 bit processors in a 64 bit environment.
In the same article, mention is made that it is a good habit to make use of 'ptrdiff_t',
which is declared in , when subtracting two pointers and using the result.
[/Personal/SoftwareDevelopment]
permanent link
2009 Apr 26 - Sun
Boost Preprocessor: Arrays
Typically, in some form of C++ best practice summaries, it is recommended to stay away
from using the C++ Macro Preprocessor. For the most part, except when I needed to
Microsoft
MFC message maps, where use preprocessor macros, I have followed this maxim. Until now.
I came across a situation where one section of code is dependent upon the order of
declarations in another section of code. With manual code preparation, and even if things
are documented appropriately, it is easy to forget to update the inter-related sections of
code properly.
An example is when initializing the column definitions of an MFC CListView. I'd like to
construct an enumeration of column indexes and ensure those remain in-sync with any changes
I may make to the CListView column defintions themselves.
I hadn't realized the power of the C++ macro preprocessor until I started reading
Appendix A: An Introduction to Preprocessor Metaprogramming in the book "C++ Template
Metaprogramming" by David Abrahams and Aleksey Gurtovoy.
By using the
Boost Preprocessor Library, the power of the C++ Macro Preprocessor is realized.
I can now define my column structures and associated variables in a single header file.
I also define various extraction macros.
#include "boost/preprocessor/tuple/elem.hpp"
#include "boost/preprocessor/array/elem.hpp"
#include "boost/preprocessor/array/size.hpp"
#include "boost/preprocessor/punctuation/comma_if.hpp"
#include "boost/preprocessor/repetition/repeat.hpp"
#define COLHDR_DELTAS_ARRAY_ELEMENT_SIZE 6
#define COLHDR_DELTAS_ARRAY \
(15, \
( \
(COLHDR_DELTAS_COL_UndSym, "UndSym", LVCFMT_LEFT, 50, std::string, m_sSymbolUnderlying), \
(COLHDR_DELTAS_COL_Sym , "Sym", LVCFMT_RIGHT, 50, std::string, m_sSymbol), \
(COLHDR_DELTAS_COL_Strk , "Strk", LVCFMT_RIGHT, 50, double, m_dblStrike), \
(COLHDR_DELTAS_COL_Expiry, "Expiry", LVCFMT_RIGHT, 50, ptime, m_dtExpiry), \
(COLHDR_DELTAS_COL_Bid , "Bid", LVCFMT_RIGHT, 50, double, m_dblBid), \
(COLHDR_DELTAS_COL_BidSz , "BidSz", LVCFMT_RIGHT, 50, int, m_nBidSize), \
(COLHDR_DELTAS_COL_Sprd , "Sprd", LVCFMT_RIGHT, 50, double, m_dblSpread), \
(COLHDR_DELTAS_COL_Ask , "Ask", LVCFMT_RIGHT, 50, double, m_dblAsk), \
(COLHDR_DELTAS_COL_AskSz , "AskSz", LVCFMT_RIGHT, 50, int, m_nAskSize), \
(COLHDR_DELTAS_COL_Pos , "Pos", LVCFMT_RIGHT, 50, int, m_nPosition), \
(COLHDR_DELTAS_COL_AvgCst, "AvgCst", LVCFMT_RIGHT, 50, double, m_dblAverageCost), \
(COLHDR_DELTAS_COL_Delta , "Delta", LVCFMT_RIGHT, 50, double, m_dblDelta), \
(COLHDR_DELTAS_COL_Gamma , "Gamma", LVCFMT_RIGHT, 50, double, m_dblGamma), \
(COLHDR_DELTAS_COL_UnRlPL, "UnRlPL", LVCFMT_RIGHT, 50, double, m_dblUnrealizedPL), \
(COLHDR_DELTAS_COL_RlPL , "RlPL", LVCFMT_RIGHT, 50, double, m_dblRealizedPL) \
) \
) \
/**/
#define COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, m, text) \
BOOST_PP_TUPLE_ELEM( \
COLHDR_DELTAS_ARRAY_ELEMENT_SIZE, m, \
BOOST_PP_ARRAY_ELEM( n, COLHDR_DELTAS_ARRAY ) \
)
#define COLHDR_DELTAS_EXTRACT_ENUM_LIST(z, n, text) \
BOOST_PP_COMMA_IF(n) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS( z, n, 0, text )
#define COLHDR_DELTAS_EMIT_InsertColumn( z, n, VAR ) \
m_vuDeltas.InsertColumn( VAR++, \
_T(COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 1, ~)), \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 2, ~), \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 3, ~) \
);
#define COLHDR_DELTAS_EMIT_DefineVars( z, n, text ) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 4, ~) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 5, ~)\
;
Then in my class declaration, I can extract the enumerations in the correct 0-based order:
enum enumColHdrDeltasCol {
BOOST_PP_REPEAT( BOOST_PP_ARRAY_SIZE( COLHDR_DELTAS_ARRAY ), COLHDR_DELTAS_EXTRACT_ENUM_LIST, ~ )
};
The repetitive code of creating the columns in the CListView is handled through repetition and
extraction macros:
int ix = 0;
BOOST_PP_REPEAT( BOOST_PP_ARRAY_SIZE( COLHDR_DELTAS_ARRAY ), COLHDR_DELTAS_EMIT_InsertColumn, ix )
// m_vuDeltas.InsertColumn( ix++, "UndSym", LVCFMT_LEFT, 50 );
I'll be able to further use the initial structure to create the row factory for keeping the CListView
and row-structures synchronized. If I happen to change my mind on column ordering, all related code sections are
automatically updated.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2009 Jan 02 - Fri
Wanted: A Single C++ Singleton
The Singleton Concept is a reasonably simple concept.
For writing software, the concept of a singleton stipulates that only one instance of a class will be instantiated during run
time. All references to an object of a particular class will be to only one instance. The instance is usually created at program
startup, often times before 'main', and destroyed at programs end, often after 'main' exits.
I wish to use the concept of a Singleton in my C++ based trading software for Manager classes which keep track of Providers,
Instruments, and Portfolios.
Thinking that the Singleton Design Pattern was simple, I figured I could implement my own flavour. But I decided to do some
research first. It turns out there are simple ways, complicated ways, and controversial ways.
From the controversial side, some consider using Singletons as
less than desirable, as the concept introduces global state, which in turn reduces modularity, compartmentalization, and as a
consequence increases the complexity of testing. These are valid reasons, and I see it being applicable to situations where a
programmer uses Singletons for small objects or built-in data types.
In my situation, I wish to form Singletons of larger self-contained classes. It doesn't make logical sense to have multiple
managers, and as such, the Singleton concept enforces/implmements my need for singleton managers.
With the Boost Libraries being as
comprehensive and well written as they are, I figured they should offer up a good singleton of a Singleton implementation. Nope. In
doing a search through the library, I find about four or six or more different flavours of singleton.hpp, and nothing in the 'common
areas' of the library:
- boost/serialization/singleton.hpp: uses boost::noncopyable, has medium multi-threading capability, and contains some .dll
dependencies
- boost/log/detail/singleton.hpp: uses boost::noncopyable, is a simpler class, not sure if it is thread safe, and makes use of
#defines like BOOST_ONCE_INIT
- boost/pool/detail/singleton.hpp: a simple, self-contained class designed for instantiation before main, basically a type of
Meyer's Singleton but using a template mechanism
- boost/thread/detail/singleton.hpp: a very minimalistic singleton
There are notes in some locations indicating that these shouldn't be used as they are essentially 'library internal' routines and
are subject to non-documented changes.
hmmm, maybe Singletons are complicated.
Alex Ott's Blog mentioned
that there was actually a
Singleton submission (documentation) to Boost back in the beginning of 2008. The submission promised
to handle single threaded and multi-threaded implementations of the Singleton Pattern. It was
rejected. Reviewers wanted to see
a more modularized approach. The submitter indicated that he has/had run out of time to do so. In reading the review thread, a number
of writers didn't like the complexity of the Loki library, and thus the Boost submission took a different tact. I'm of an
impression that the library submission would have a good change of succeeding if it followed the
'programming by policy' method used in Loki. Under the hood there is some complexity, but to the user, the interface is clean and modular.
For those interested in the submission code, It resides mouldering in the
sandbox.
Loki, which was started by
Andrei Alexandrescu in his book on "Modern C++ Design", has a Singleton
implementation, but no on-line documentation. I went into his book and I see that he goes into some detail on the design ideas and
usage notes regarding his Loki SingletonHolder class. Indeed, he says that there is no single size-fits-all singleton. And when
looking at the doxygen class notes, there is a variety of construction, lifetime, and threading template traits available. Perhaps
this file could be spruced up and submitted to Boost.
Andrei and Scott Meyers wrote a paper back in September 2004 called
C++ and the Perils of Double-Checked Locking. It goes into the gory details of why multi-threaded Singletons are so hard to
implement. Much of it has to do with compiler optimizations, and the fact that C++ machine states are defined for single threaded
models only. It is interesting to note that implication that C++ is really multi-threaded, from a philosophical and design
perspecitve. It has been forced into that world with assemlby code and operating system api work arounds. C++ has been so malleable
when it comes to metaprogramming, object oriented programming, and any of a number of other programming paradigms. To fall down on
the job of multi-threading may be an indication of the difficulties inherent in moving from single-threading to
multi-threading and multi-core processing.
In addition to the library submitted to Boost, another author offers up his version of a
Thread-Safe C++ Singleton. His writing indicates he
uses the concept of a Phoenix Singleton, a Singleton which can recreate itself. The book Modern C++ Design goes into a description
of this.
For a simple, single-threaded, self contained Singleton which manages itself, a
C++ Singleton Pattern is available. It uses the
Curiously Recurring Template Pattern
(CRTP). It uses an override of new and delete but does not use reference counting to keep things straight, which may cause
problems in some use cases. It is like a Phoenix Singleton but doesn't really do LIFO type creation/destruction properly.
Scott Meyer's "Effective C++ Third Edition" has a description of what has been termed Meyer's Singleton. It does all the
constructor, destructor, and assignment hiding and provides a built-in static method for returning a reference to the object
instance. Here is a specific version of
The Meyers Singleton.
class InstrumentManager {
public:
static InstrumentManager &Instance() {
static InstrumentManager _InstrumentManager; // local static object initialization
return _InstrumentManager;
}
void BasicMethod( void );
private:
InstrumentManager(); // constructor (ctor) is hidden
InstrumentManager( InstrumentManager const & ); // copy ctor is hidden
InstrumentManager &operator=( InstrumentManager const & ); // assignment operator is hidden
~InstrumentManager() // destructor (dtor) is hidden
};
A Generic Meyers Singleton:
// singleton.h
#ifndef __SINGLETON_H
#define __SINGLETON_H
template class CSingleton {
public:
static T& Instance() {
static T _instance;
return _instance;
}
protected:
private:
CSingleton(); // ctor hidden
~CSingleton(); // dtor hidden
CSingleton(CSingleton const&); // copy ctor hidden
CSingleton& operator=(CSingleton const&); // assign op hidden
};
#endif
In summary, I think I'll end up using the boost::detail::pool as it can be used to wrap general classes without resorting to
writing classes as specific Meyers Singletons, which may or may not be a good thing. If, at some point in the future, I get
some free time, tackling the Loki Singleton to Boost Singleton conversion might be an interesting learning experience.
See also Singleton Per Thread.
[/Personal/SoftwareDevelopment/CPP]
permanent link
2008 May 29 - Thu
Evaluating Inter-Process Communication Frameworks
I'm reposting some comments regarding IPC frameworks that I made to the Boost-Users
listserve today. It is in response to someone making unsubstantiated remarks regarding the
relative merits of ACE and Boost, and another looking for some substatiated remarks. What
follows are some substantiated remarks, based upon my personal experience with it and
several other libraries.
I've started working on a number of distributed system projects. As a consequence, I
started looking for distributed system libraries. References to ACE were most pervasive. I
implemented a number of trial applications with the library. That was after plowing through
relevant sections in the three primary ACE reference books. That was a good learning
experience, if only to find out the various patterns in distributed architecture definition.
I had the inter-process/inter-server communications (which only sent simple stuff) working
well within ACE's Acceptor/Connector framework. ACE has a number of other patterns one can
use. I was really impressed with the fact that the examples I used from the books worked as
advertised, and I was able to bend them to my will.
ACE is based upon an interaction of classes, macros, and templates. One has to spend
some
time with the environment in order to become proficient with it. It has a large API. A
number of lower level API's upon which higher level API's are based. For example the
Acceptor/Connector uses constructs described earlier in the books.
Once I had my basic communications going, I realized I needed to get some form concrete
messaging infrastructure in place. I had an impression that TAO, which is a layer above
ACE, would be quite extravagant to implement, with it being an implementation of the CORBA
specification. I wanted something a little lighter (a whole lot lighter actually).
As I worked through that project, I started hearing about ASIO, indirectly through some
other libraries I was using. ASIO is now a member of Boost. I read a review somewhere that
ASIO is a 'modern' replacement for ACE. If you want to get into real template structures
and Boost oriented philosophy, I'd say that is a valid statement. I'd also say that ASIO is
'more to the point' and straight forward than is ACE, at least for the things I want to
accomplish. But like ACE, ASIO is the basic communications infrastructure, no real
messaging capability, which is what distributed computing is all about. ASIO turned out to
be a little harder to get my head wrapped around as it uses a number of advanced C++ and
Boost related idioms. For a run-of-the-mill C++ programmer, ACE would be better. For
someone steeped in the power and obscurity of advanced C++, and is looking to advance their
skill set, ASIO would be better.
I came across
RCF - Interprocess communication for C++, which is a
messaging framework riding atop of ASIO. Flexible, lightweight, and to the point. I worked
through the examples and things worked as advertised. It has the encryption,
publisher/subscriber, oneway/twoway idioms, and a few other nifty features.
At the same time I was doing that, seemingly coincidently, I learned a few more
interesting
facts. Going into this, I realized that I need a message dispatcher/proxy, some decent
failover techniques, and some additional event handling for non-IPC related activities.
Someone suggested ICE from www.zeroc.com for an RCF-like solution, but working to a
larger
scale. I've heard that the library's originator is someone who spent much time on CORBA
standards and redid the concept without the 'benefit' of committee involvement. I think the
library has all the bases covered in terms of lightweight message handling, dispatching,
resiliency, and higher level distributed processing philosophies. The drawback is that it
will have a steeper learning curve than would an implementation using RCF. I like RCF, but
I think I.m going to have to tilt towards ICE (itself, like RCF, developed and focused
towards C++ in a multiple license environment).
On the non-IPC front, Qt's QCoreApplication looks to be a good substrate on which to
build
event driven daemons.
In the end, I think my solutions are going to involve:
- ZeroC's ICE for primary inter-process
communications
- Qt QCoreApplication as a
base for daemon development (which has built-in stuff for
threads/locks, slots/signals)
- Wt, a C++ based web toolkit for
distributed GUI development
- Boost Libraries to fill in all the
holes
- a little legacy layer 3/4 ACE in one library I'm using, but with some work, I think I
can
convert the ACE stuff to ASIO
[/Personal/SoftwareDevelopment/CPP]
permanent link
|