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. cover


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
Username:
Email: *
Password:
Confirm Password:
* Information will not be shared with 3rd parties

Already Registered?
Click Here to Login.

-- CProgrammingTrends is an iEntry, Inc. publication --
2003 iEntry, Inc. All Rights Reserved Privacy Policy  Legal