07.28.03 
By
Ray Lischner
Once you have mastered the C++ language and the standard library, what do you
tackle next? The C++ community has plenty to keep you busy for years to come.
To start with, you might take a look at the many C++ libraries that extend the
standard. Sure, the C++ library has strings, I/O streams, containers, iterators,
algorithms, and all that fun stuff. But it lacks much that is commonplace in modern
computing: networks, GUIs, concurrency, and so on.
As a result, numerous C++ library projects have arisen to complete, correct,
or extend the standard C++ library. Some were started even before the C++ standard
was complete; others arose later. This article highlights a number of open source
C++ libraries, including ACE, Loki, and Boost. (Many other projects exist, of
course, but it is not possible to present them all in a single article.) If you
have time to learn only one extra library, I recommend Boost. Numerics
Two projects, Blitz++ and Matrix
Template Library (MTL), provide high-performance numeric computing. In a way,
they represent what valarray should have been. |
The Blitz++ project was one of the early projects to take advantage of templates
to shift part of the computing burden from runtime to compile time. It has powerful
array and matrix classes, operators, and functions, with strides, subarrays, and
so on.
One of the key optimizations is that arithmetic operators and mathematical functions
involving Blitz++ arrays do not compute values immediately. Instead, they return
expression objects. When an expression object is assigned to an array, the expression
is computed, storing the results directly in the target of the assignment, without
the need for large temporary arrays.
The MTL also uses templates to optimize performance. Its approach is more reminiscent
of the STL, using containers (such as Matrix and Vector), iterators, and generic
algorithms (such as transpose, dot (dot project), and so on). ACE
ACE (ADAPTIVE Communication Environment) has been around for a long time, and
provides a framework for networks, communication, and concurrency. It is well
documented in two books: C++
Network Programming,: Mastering Complexity with ACE and Patterns, and
C++
Network Programming: Systematic Reuse with ACE and Frameworks, both by
Douglas Schmidt and Stephen Huston.
ACE is big. It does a lot, and it does a lot for you. It has concurrency, synchronization
primitives, sockets, and related network classes. ACE also comes with TAO (The
ACE Orb), for CORBA support. Most important, ACE is an integrated framework, where
the networking and concurrency work together.
At the bottommost layer, ACE has a portable API to interface with the operating
system (Windows, Linux, and many other UNIX variants). On top of this API is a
set of C++ wrapper classes. The wrappers include synchronization primitives (such
as Mutex and Semaphore), interprocess communication with sockets and pipes (such
as ACE_SOCK_Connector, ACE_SOCK_FIFO, and ACE_SOCK_Addr), and concurrency (such
as Future and Servant thread classes).
The next layer up is the main framework. The Reactor and Proactor classes dispatch
messages in an event-oriented fashion, integrated with all the ACE synchronization
classes. At this level, the ACE Orb is also integrated for CORBA.
You can download the source code from the main Distributed
Object Computing web site at Washington University. (Follow the ACE link.)
Loki Loki
was invented by Andrei Alexandrescu and is documented in his book, Modern
C++ Design. It pushes templates to their limits (and beyond for many compilers),
showing the power of templates for policy-based programming and metaprogramming.
Unlike many other projects, Loki was developed on Windows, not Unix, and the files
are DOS text files.
To see the benefits of policy-based programming, consider the problem of implementing
a singleton class. A singleton class is a class for which no more than one instance
can exist at runtime. Different needs for singletons give rise to different implementations.
For example, sometimes you might want a single, static instance. Other times,
you might want lazy instantiation, so the singleton object is not created until
it is needed, but then is not destroyed until the program exits. Other times,
you might want a dynamic instance that is destroyed when it is no longer needed,
only to be recreated when it is needed again. A single class cannot easily meet
these different demands, but a policy-based template can.
Loki declares the SingletonHolder template, which can be used to manage a singleton.
You provide a type as a template parameter, and the SingletonHolder creates the
singleton instance for you, which it returns from its Instance() member function.
Other template parameters specify the policies that govern the singleton.
template
<
typename T,
template <class> class CreationPolicy = CreateUsingNew,
template <class> class LifetimePolicy = DefaultLifetime,
template <class> class ThreadingModel = SingleThreaded
>
class SingletonHolder
{
public:
static T& Instance();
private:
// Helpers
static void MakeInstance();
static void DestroySingleton();
// Protection
SingletonHolder();
// Data
typedef typename ThreadingModel<T*>::VolatileType PtrInstanceType;
static PtrInstanceType pInstance_;
static bool destroyed_;
};
The CreationPolicy determines how the singleton instance is created. Loki provides
CreateUsingNew, CreateUsingMalloc, and CreateStatic. The latter constructs the
singleton object in a static buffer. You can supply a custom creation policy by
writing your own class template that provides static Create() and Destroy() member
functions. The SingletonHolder takes care of the details of when Create() and
Destroy() are called. Following is the CreateUsingNew policy: template <class T> struct CreateUsingNew
{
static T* Create()
{ return new T; }
static void Destroy(T* p)
{ delete p; }
};
The LifetimePolicy determines the singleton's lifetime. The DefaultLifetime destroys
the singleton object when the program exits (using an atexit handler). If the
program attempts to refer to the singleton object after it has been destroyed,
the LifetimePolicy object throws an exception. The PhoenixLifetime policy is similar,
except it allows the singleton to be recreated after it has been destroyed.
The SingletonWithLongevity policy lets you control the relative lifetimes of multiple
singletons. A longevity, in this case, is an unsigned integer. Objects with higher
valued longevity are destroyed after objects with lower valued longevity. You
must provide a free function, GetLongevity, that returns the longevity of an object.
Finally, the NoDestroy policy never destroys the singleton object. Use of this
policy can result in memory or other resource leaks. To implement your own lifetime
policy, write a class template that has static ScheduleDestruction and OnDeadReference
functions. For example, the following code is the DefaultLifetime policy:
template <class T>
struct DefaultLifetime
{
static void ScheduleDestruction(T*, void (*pFun)())
{ std::atexit(pFun); }
static void OnDeadReference()
{ throw std::logic_error(std::string("Dead Reference Detected")); }
};
The final template parameter is the ThreadingModel, which can be SingleThreaded or
ClassLevelLockable. The first does not perform any locking, and is suitable for
single-threaded applications. ClassLevelLockable creates a lock for each class.
Loki also has ObjectLevelLockable, but SingletonHolder is never instantiated because
you use only the static Instance() function. Thus, you have no reason to use ObjectLevelLockable
in this case.
To use SingletonHolder, start by declaring your class. Be sure to make the constructor
private, with the creation policy as a friend, or otherwise ensure that the public
cannot circumvent SingletonHolder and create arbitrary instances of your class.
Then instantiate SingletonHolder with your type as the first template argument.
You can use the default values for the remaining template arguments, or choose
one of Loki's policy implementations, or write your own. The following is a simple
example: #include <cassert>
#include "Loki/Singleton.h"
class thingy {
thingy() {}
template <typename T> friend class Loki::CreateUsingNew;
public:
};
typedef Loki::SingletonHolder<thingy> single_thingy;
void foo(thingy& t)
{
assert(&t == &single_thingy::Instance());
}
int main()
{
foo(single_thingy::Instance());
}
Boost
I saved the best for last. Some members of the C++ standard committee started
Boost as a platform for exploring future directions
for the C++ standard library. Already, parts of Boost are being added to the standard
library (such as type traits, regular expressions, smart pointers, enhanced binders,
and adapters). You can expect to see other parts of Boost added in the future.
Boost is big and eclectic. It contains everything from type traits to quaternions
(a generalization of imaginary numbers to four dimensions), from parsing to linear
algebra, from simple memory utilities to threads and synchronization.
This section presents only a few of the packages in Boost that are part of the
proposed library extension: tuples, smart pointers, lambda expressions, and the
Spirit parser generator. Building and Installing Boost
The web site has instructions for building and installing Boost. You need to install
Boost Jam, which is like make, but is designed for greater portability
and ease of use. Follow the directions on the web site to build and install Boost.
Tuples
A tuple is a generalization of the standard pair class template. Instead
of being limited to two elements, a tuple can contain up to 10 elements (and the
maximum can be extended if necessary). The following example shows one way to
use boost::tuple: #include <numeric>
#include <iostream>
#include <ostream>
#include <vector>
#include "boost/tuple/tuple.hpp"
// Store count, sum, and sum of squares.
typedef boost::tuple<std::size_t, double, double> Stats;
// Accumulate statistics.
Stats stats(Stats s, double x)
{
++s.get<0>();
s.get<1>() += x;
s.get<2>() += x * x;
return s;
}
int main()
{
std::vector<double> v;
... fill v with data ...
Stats s = std::accumulate(v.begin(), v.end(),
boost::make_tuple(0U, 0.0, 0.0), stats);
std::cout << "count = " << s.get<0>() << '\n';
std::cout << "mean = " << s.get<1>() / s.get<0>() << '\n';
}
Smart Pointers
Boost has several smart pointer class templates. They solve a number of problems
that the standard auto_ptr<> class template does not. For example, you cannot
store an auto_ptr<> object in a standard container, but you can store a
boost::shared_ptr<> object. Boost has several other smart pointer templates;
for the sake of brevity, the following example only shows shared_ptr<>:
Click
Here to Read the Full Article
About the Author:
|
C++ in a Nutshell packs an enormous amount of information on C++ (and the
many libraries used with it) in an indispensable quick reference for those who
live in a deadline-driven world and need the facts but not the frills. Cross-references
link related methods, classes, and other key features. This is an ideal resource
for students as well as professional programmers. |
 |
Read this Newsletter at: http://www.cprogrammingtrends.com/2003/0728.html |
|
 |
| • |
Get
in-touch with industry experts and leaders |
| • |
Make
Freelance contacts |
| • |
Post
your site for review by expert and peers |
| • |
Ask
SEO, Marketing, Development and Design questions |
| • |
Join
a community of your peers in all fields, ready to help |
|
|
|
|
|