Google

The UDS Collection

Features

back to main page

This section gives a brief description of the features UDS provides. Sometimes the description is very briefly. Have a look at the class reference and / or the header files.
Have also a look at the example source code in the test directory of the source distribution.
Note:
Don't forget to read how to configure UDS in your programs


Automatic search for memory leaks

When enabled, UDS keeps track of all memory allocations and deallocations. When the program exits, it is checked whether all memory has been freed. If not, a message like the following is printed to stderr. (see test/example1.cc in the source distribution)

WARNING: 2 memory leak[s] detected
size   address    source             function
-------------------------------------------------------------------------------
4      0x808f228  example1.cc:38     main
8      0x808f238  example1.cc:39     main

It is possible to set the number of expected memory leaks. This is useful since some libraries or programs allocate a fixed amount of memory which is never freed.
The following code snippet shows how to set the number of expected leaks to 2

#include <uds/sentinel.hh>

...

uds::sentinel().Expect( 2 );

Note that it is possible to set several numbers of expected leaks.
You can also filter memory leaks by filename using the Sentinel::Ignore method. The following snippet shows how to ignore all memory leaks which are caused by functions defined in files which contain the substring 'include' in its path.

#include <uds/sentinel.hh>

...

uds::sentinel().Ignore( "include" );

If you link against shared libraries you might want to call Sentinel::IgnoreUnknown to ignore all memory leaks with unknown origin.


Logging of memory (de)allocations

Sometimes it is necessary to log all memory (de)allocations. This option does exactly this. When enabled, messages like the following will be printed to stderr. (see test/example2.cc in the source distribution)

function at address 0x804a7b0 allocates 4 bytes at address... 0x8058218
0x804a7c4 releases memory at 0x8058218... 4 bytes of memory allocated by 0x804a7b0 freed


Zombie objects

When memory is deallocated, the contents are usually not changed until the memory is allocated again. Sometimes this shadows bugs when objects are in use which seem to be intact, but have already been destroyed.
When this option is enabled, freed memory is overwritten. If the size is dividable by the size of a pointer (there could be an object with virtual functions) the memory is filled with pointers to a virtual function table that contains only pointers to a function that throws a fatal exception. That looks like the following. (see test/example3.cc in the source distribution)

(6635) coredumping...
zombie object invoked:
virtual function call failed (fool: 0x8130838)
--- UDS backtrace (6635) ---
exception.cc:150      uds::Exception::dump()
exception.cc:68       uds::Exception::Exception(bool, std::string const&, uds::
                      DiagBase const&)
stdexcpt.hh:40        uds::zombie_object::zombie_object(std::string const&, uds
                      ::DiagBase const&)
sentinel.cc:53        uds::throw_zombie(void*)
example3.cc:52        main


Backtraces at runtime

The Backtrace() function (declared in uds/btrace.hh) returns the backtrace as a string. By default source files, function names, and line numbers are printed instead of memory addresses (see test/example12.cc). If you want just the memory addresses, pass false as the fist argument to Backtrace (see the reference).
This function is very useful for debugging. test/example13.cc shows how to use the Backtrace function in a signal handler.
Example output:

entered segfault handler
--- UDS backtrace (6684) ---
example13.cc:37       segv_handler(int)
sigaction.c:149       __libc_sigaction
example13.cc:52       foo()
example13.cc:60       main
Segmentation fault (core dumped)

This function is also used by the UDS exception classes (see below). If the std_backtraces flag is set, a backtrace is always generated when a UDS exception is thrown. You can retrieve it with the Exception::Backtrace method. It is also part of the standard error message (Exception::Message).
Since only the executable is checked, symbols from shared libraries can not be resolved (static libs work fine).


Exception system for (almost) fatal errors

When there is a fatal error, you can eg print an error message and call exit or abort. When you call abort, you have a core dump that can be used to track down the problem, but no cleaning up is done since global objects are not destroyed and no stack unwinding is done. When you call exit at least global objects are destroyed, but you have no core. When you throw an exception, all objects are destroyed, but you have usually no backtrace, so you have to figure out where the exception was thrown.
UDS provides exception classes that can dump a core when they are thrown by calling fork and abort. So you have both a core dump and stack unwinding. There are also a few exception class generation macros.

Note that you should place an instance of the uds::Init class in your main function. If an instance was not created, it is assumed that an exception might not be caught in case of a fatal error. Therefore an error message is printed to stderr before the exception is thrown.

All UDS exception have a diagnose object that may contain additional information (eg the value of the errno variable when a standard C library function failed, or the exit status of a child process that terminates unsuccessfully) and can give an error diagnose. The Diagnose() method is used to retrieve this diagnose from an exception class, while the Message() method produces a complete error message. It is also possible to get direct access to the diagnose object with the Diag() method.
Since numeric error values are very common, all diagnose classes provide the following virtual functions: HaveErrCode() returns true if the diagnose object provides an error code. ErrCode() is used to retrieve the error code. The meaning of this value depends on the diagnose class.

Starting with version 0.9.5, UDS exceptions can store a backtrace which can be accessed via Exception::Backtrace. The backtrace is also added to the error message generated by Exception::Message.
By default, the backtrace is only generated if a fatal error occurs (that means if a core is dumped, see above). However, you can use the std_backtraces flag to override this behavior. See Configuring UDS for details.


Action, FinalAction, and VRemember

FinalAction and VRemember are useful classes, especially when you want to write exception safe code. The constructor of FinalAction takes a function / function object that will be called when the FinalAction instance is destroyed. This is a convenient way to specify code that is always executed when the function is left (no matter whether it returns normally or an exception is thrown).
The VRemember class takes a reference to a variable and remembers the current value. When the instance is destroyed, the original value is restored. When a second argument is passed to the constructor, it is assigned to the variable (after the old value was copied).
The Action template class is a wrapper for functions / function objects. Its only template parameter is the return type of the function [object]. Action classes are useful when you 'store' function calls (like cleanup handlers): my_atexit( const Action< void >& ) is much more flexible than something like my_atexit( void ( * )( void* ), void* ) since you can use a function object with any arguments, not just one void* argument.
Note that Action< void > means that the return type of function[object]s doesn't matter - it doesn't have to be void. AnyAction is a typedef for Action< void >.


Function objects that are more flexible than STL function objects

The STL defines a set of useful function objects. However they support only unary and binary functions. Furthermore, you get into problems when you want to use STL binder classes with references.
UDS provides function objects that can easily be created with calls to uds::function and uds::function_ref. Binder or Adaptors are not required.
The first argument to uds::function is the function to be called. That can be an ordinary function, or a member-function. The arguments to that function are passed to uds::function as function objects. When the object returned by uds::function is called, the argument function objects are called to retrieve the arguments.
Have a look at the following example (test/example4.cc in the source distribution).

void
print( ostream& out, const string& s )
{
	out << s;
}

int
main()
{
	uds::FinalAction x( uds::function( &print, uds::reference( cout ), uds::value( "bar\n" ) ));
	print( cout, "foo" );
	
	return 0;
}

This will print 'foobar'. Note that the first argument, cout, is copied by reference while the second argument is copied by value.
When x is destroyed, it calls the function object without an argument. The real arguments of print are retrieved via calls to the function objects that were passed to x.
However, it is possible to call UDS function objects with one argument. This makes it possible to use them in STL algorithms like for_each(). In this case, the argument function objects are called with the argument to the object returned by uds::function. Some function objects (like those returned by uds::value or uds::reference) will simply ignore the argument. (see test/example5.cc in the source distribution).
typedef map< int, int* > Map;
Map m;
m[3] = new int();
m[6] = new int();

cout<<"deleting all map values...\n";

for_each( m.begin(), m.end(),
	uds::function( Delete< int >,
	uds::select2nd< Map::value_type >() ));


Threads, Mutexes, Semaphores

UDS provides several wrapper classes for Posix Threads. They include Thread and Thread Attribute classes, Mutexes, Condition Variables, and Semaphores. The classes provide methods for the pthread_* functions with a few (two) extensions. Have a look at the reference or uds/thread.hh. The class definitions are pretty straightforward (you should know Posix Threads though).
Thread instances can only be created on the heap. They maintain a reference count and should be used with garbage collection smart-pointers (see below). You don't have to delete them manually; the reference count is decreased automatically when the thread exits.
Have a look at the following example (test/example8.cc):

void*
thread_start()
{
	cout << "new thread started\n";
	sleep( 1 );
	cout << "leaving new thread\n";
	
	return 0;
}

void
cleanup( int a, double b )
{
	cout << "cleanup " << a << " / " << b << endl;
}

int
main()
{
	Thread& t = *new Thread( &thread_start );
	t.PushCleanupHandler( function( &cleanup, value( 4 ), value( 3.68 ) ));
	sleep( 2 );
	cout<<"leaving main thread\n";
	
	return 0;
}

The result is:
new thread started
leaving new thread
cleanup 4 / 3.68
leaving main thread

This shows extension #1: Thread::PushCleanupHandler and Thread::PopCleanupHandler are much more flexible than their pthread_* counterparts: They don't have to appear as pairs in the same function, at the same level of block nesting. Cleanup handlers that are still on the stack when the thread exits are executed. Since an Action instance is specified as function to be called you are not limited to one void* argument.
When you construct the thread you pass an Action< void* > as start routine (extension #2).
Noteworthy are also the MutexLock and CMutexLock classes that lock a mutex when they are created, and unlock the mutex when they are destroyed. This is an easy way to lock mutexes in an exception safe manner. CMutexLock registers a cleanup handler to unlock the mutex. You can achieve the same with FinalActions, but the MutexLock classes are easier to use.


Socket Classes

Socket classes for both connection and packet oriented protocols are provided. Supported protocols are TCP (TCPSocket; streamsocket.hh), Unix Domain sockets (stream; UnixSocket; streamsocket.hh), UDP (UDPSocket; packetsocket.hh), and X.25 (X25_SWanpipe; swanpipe.hh; Sangoma wanpipe cards under Linux only).
The base class for stream sockets, StreamSocket, is derived from iostream so you can use all standard stream conversion operators, stream manipulators, and UDS stream functions (like the BRead / BWrite and ReadLine families of functions). There is also an additional stream manipulator, sockflags, which can be used to change the flags which are passed to send() and recv().
The Listen(), Accept(), and Connect() methods are pretty much the same as their system call counterparts. See the reference, test/example10.cc, and test/example14.cc for more details and examples.


ProcStream class

The ProcStream class provides file stream operations for connections to child processes. It was designed as replacement for popen(), but is more flexible. Since a pair of anonymous unix-domain sockets is used instead of pipes both read and write operations are supported. Furthermore it is possible to specify the environment of the child process. As required by POSIX.2 for popen(), Streams from other ProcStream instances that remain open in the parent process are closed in the new child process. The use of this class is pretty straightforward. See the reference or uds/procstream.hh, and test/example11.cc for more information.


Classes for reference counting that make it easy to implement copy-on-write and garbage collection

UDS provides base class templates for reference counting, and smart pointers for garbage-collection and copy-on-write. To make a class use reference counting, derive it from one of the uds::RefCounter templates. The template arguments are the type that is used to hold the reference count (size_t by default) and a flag that indicates whether it is possible to mark objects as unshareable. Unshareable objects are always copied instead of increasing the reference counter.
For simple garbage collection use the uds::GC_Ptr template. Arguments are the wrapped class and a flag that indicates whether those class defines the method Clone(), which is used to copy the object instead of calling the copy constructor directly. (see test/example6.cc)

// class that uses reference counting

class RefC : public uds::RefCounter<>
{
public:
	RefC() { cout<<"constructor\n"; }
	~RefC() { cout<<"destructor\n"; }
};

typedef uds::GC_Ptr< RefC > GC_Obj;


GC_Obj
new_object()
{
	GC_Obj foo = new RefC();
	// the reference count of the newly created object is now 1
	GC_Obj bar = foo;
	// the reference count is now 2
	
	return foo;
}

int
main()
{
	// This object must not be destroyed manually
	// It will be destroyed when the reference count reaches 0.
	GC_Obj x = new_object();
	// the pointers in new_object() were destroyed; the reference
	// count is now 1
	
	return 0;
}

This prints
constructor
destructor

For copy-on-write (actually copy-on-maybe-write, see below), use uds::CoW_Ptr. Template arguments are the same as GC_Ptr.
(see test/example7.cc)
// class that uses reference counting

class RefC : public uds::RefCounter<>
{
public:
	RefC() { cout<<"constructor\n"; }
	RefC( const RefC& ) { cout<<"copy constructor\n"; }
	~RefC() { cout<<"destructor\n"; }
	
	void
	func() const {}
};

typedef uds::CoW_Ptr< RefC > CoW_Obj;


CoW_Obj
new_object()
{
	return new RefC();
}

int
main()
{
	CoW_Obj x = new_object();
	const CoW_Obj y = x;
	
	cout<<"calling member function through constant smart pointer\n";
	y->func();
	
	cout<<"calling member function through non-constant smart pointer\n";
	x->func();
	
	return 0;
}

The output of the program is
constructor
calling member function through constant smart pointer
calling member function through non-constant smart pointer
copy constructor
destructor
destructor

Note that if you call a const method through a non-const CoW_Ptr, the wrapped object will be copied. Therefore you should use const CoW_Ptrs whenever possible.


Simple random number generators

UDS provides a few simple random number generator classes. However, they are not much more than rand() replacements. While they are good enough for most games and simple apps, you should not use them in complex mathematical programs.

  • RandInt is used to generate random numbers between 0 and 2^31, and floats between 0 and 1.
  • URand generates random numbers (ints and floats) between 0 and a maximum
  • ERand generates exponentially distributed random numbers between 0 and 2^31
They are simple to use; have a look at the class reference or uds/random.hh.


Several "convenience functions" to create temporary file names; open files, fork, wait etc. and throw an exception if something goes wrong

UDS provides a few convenience functions, that perform system operations and throw an UDS exception if the function fails. The exception contains error descriptions according to the value of the errno variable.
For a list and documentation of the convenience functions, have a look at the class reference or uds/sys_util.hh.


Logging class

UDS provides a logging class which can send log messages to logfiles and the system logger. You can set different log levels for both types of output. If the priority of the message to be sent is less than the log level, it gets logged.
When specifying priorities or log levels, you can use the constants defined by syslog ranging from LOG_EMERG (0) to LOG_DEBUG (7). However, values up to 255 are supported.
See the class reference and example16.cc for more information.


Alternative file stream class

The FileStream class works basically the same way as standard fstreams do, with a few extensions:

  • Additional flags:
    excl
    exclusive file creation (equivalent to O_CREAT)
    create
    create file if it does not exist. Does not require ios::trunc
  • independent input / output file positions
  • direct access to the file descriptor (to lock the file etc.)
See the reference and example15.cc for more information.