Welcome to TTL (the Tiny Template Library)

Download

For discussions about the library, support or feature requests, use the mailing list.

If you'd like to contribute, please e-mail the project admin.

Overview

The Tiny Template Library (TTL) is a C++ template library for generic programming. The main objective is to develop a lightweight practical alternative to some of the Boost components. TTL started as an experiment during the development of Notus, a GUI template library. TTL was heavily influenced by Boost. As much as possible we try to support the Boost interfaces and semantic. In most cases switching between Boost and TTL should be as easy as switching headers and namespaces. For example

#if defined(__USE_BOOST_VARIANT__)

# include "boost/variant.hpp"

# define VAR boost

#else

# include "ttl/var/variant.hpp"

# define VAR ttl::var

#endif

VAR::variant<int, char> v(1);

Note: the comments about boost and ttl reflect our experience with boost v1.31.0 and TTL v1.0.

Installation

Download

The whole library resides in headers files only. There is nothing to be built or installed, just copy the TTL files into one of you folders and and make sure that this folder is in the list of include folders in your compiler. You can then include TTL headers as in the following example.

#include "ttl/var/variant.hpp"

Boost doesn't have a consistent approach to the memory management. Some classes use allocators, while others use global new/delete.

Boost doesn't have a consistent exception handling policy. I would argue that some of the classes are not safe. For instance some components use try/catch(...) internally catching all possible exceptions that might happen on the system and so triggering the stack unwinding in a potentially unsafe environment.

Long compilation times even for seemingly simple classes.

I didn't want to link to a static library to get a simple signal-like functionality.

A heavy use of macros and #if/#else clauses make it very difficult to read and debug boost sources. One of the Boost's objectives is to support as many compilers as possible including non-compliant compilers such as MSVC v6.0. The result is that there are many flavors of the same object that are separated by nested #if/#else blocks such as //real C++ version is already taken care of

#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)

Design Notes

One of the main objectives is a compact and readable implementation. The library requires a relatively compliant compiler. As for now, we test TTL on two compilers, MSVC v7.1 and GCC v3.2.3. If you are still using MSVC v6.0, please come back after you get rid of it.

One definition

All classes have only one definition. For example if a definition compiles on MSVC v7.1 but not GCC 3.2.3, we'll choose to change the definition instead of adding #if defined(_MSC_VER) block.

Memory Management

TTL will never use global new/delete. All memory allocations/deallocations will be done with a user defined allocator or allocator defined in ttl::mem::memtraits class.

Exception Handling

TTL never throws an exception type other than derived from the ttl::exception class. The library will never attempt to catch all exceptions with catch(...). Internally the library is allowed to catch std::exception derived exceptions only.

Sample

To build the sample, make sure to set TTL_ROOT environment variable to your TTL folder. The sample is located in the samples/test folder.



When ran, it should output

int event: 10

double event: 2.3

The sample source code:

#if defined(_MSC_VER)

# include <crtdbg.h> //just to generate the memory dump

#endif



#include <iostream>

#include "ttl/func/function.hpp"

#include "ttl/sig/signal.hpp"

#include "ttl/var/variant.hpp"



namespace VAR=ttl::var;

namespace SIG=ttl::sig;

namespace SIG_CON=ttl::sig;

namespace FUNC=ttl::func;

typedef ttl::sig::connection connection;



//define event type

typedef VAR::variant<double, int> event;



struct event_visitor

{

void operator()( int x )

{

std::cout<<"int event: "<<x<<"

";

}

void operator()( double x )

{

std::cout<<"double event: "<<x<<"

";

}

};

void event_callback( event& ev )

{

event_visitor v;

VAR::apply_visitor(v, ev);

}



main()

{

#if defined(_MSC_VER)

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

#endif

//create signal and connections

SIG::signal< void (event&) > sig;

connection con = sig.connect( event_callback );



event ev(10);

sig(ev);

ev = 2.3;

sig(ev);

//disconnect

con.disconnect();

}







The flg namspace contains:

flags

flag_mapper

ttl::flg::flags

flags<> provides a type safe and generic way to handle collections of flags that are usually defined as bitfields

template< typename T, //flags type usuall enumeration int Bits = sizeof(int)*8, //number of bits typename Holder = typename impl::bestfit ::type //hold combinations of flags > struct flags { typedef flags this_t; typedef T value_type; Holder f_; flags(); flags( T f1, T f2, ... ); //intializes to f1|f2... Holder get_holder() const; bool test( const this_t& ) const; //applies operator & to the holders bool test() const { return f_!=0; } };

Usage

The flags<> class supports all bitwise and logical operatos.

//define some flags struct winstyle { enum type { CAPTION = 1, BORDER = 1<<1, size = 2 //the last used bit }; }; typedef flags<winstyle> winstyle_flags; void set_style( winstyle_flags style ) { if( style.test(winstyle::CAPTION) ) ...do something about CAPTION } main() { //initialize to winstyle::BORDER|winstyle::CAPTION winstyle_flags f(winstyle::BORDER, winstyle::CAPTION); set_style( f ); }

ttl::flg::flag_mapper

The TTL has a function for mapping one set of flags to another set. For instance if we need to map our winstyle flags to Win32 WS_CAPTION and WS_BORDER.

struct flag_map { std::multimap< std::pair<winstyle_flags, int> > map; flag_map() { if( init_ ) { init_ = false; //initialize only once map_.insert( winstyle::CAPTION, WS_CAPTION ); map_.insert( winstyle::BORDER, WS_BORDER ); } } static bool init_; static map map_; } void set_style( winstyle_flags style ); main() { flag_map map; set_style( winstyle_flags(winstyle::CAPTION,winstyle::BORDER) ) } void set_style( winstyle_flags style ) { //map our flags to Win32 int style = ttl::flg::flag_mapper( flag_map.map_.begin(), flag_map.map_.end(), style ); ::CreateWindow( ... style ); //use Win32 function with 'style' :) }

The tup namespace implements tuple. Its semantic is very similar to boost::tuples. However the implementation concepts are quite different.

The tup namespace contains the following symbols:

tuple<>

get<>

length<>

element<>

The maximum number of fields in tuple is defined by the TTL_MAX_TUPLE_PARAMS macro in config.h

Usage

ttl::tup::tuple<int, double> my_tuple;



my_tuple t(1, 2.3);

int n = ttl::tup:get<0>(t);

double x = ttl::tup:get<1>(t);

int l = ttl::tup::length<my_tuple>::value;

assert(l==2);



ttl::tup::element<N, Tuple>::type //type of the Nth element in Tuple.



ttl::meta

The meta namespace provides a small set of meta programming facilities.

typlist<>

get<>

type_switch<>

ttl::meta::typelist

typelist is is a list of types that is specified as a set of template parameters. typelist<int, char> is a list of 'int' and 'char' types. typelist is very efficient. It is implemented as a plain set of typedefs. It doesn't have the overhead associated with nested templates as in some more popular implementations that use cons<H, cons<H, ...> > or duo<H, duo<H,...> > kind of techniques.

There is a number of meta functions that operate on typelists.

ttl::meta::get<L, N>

typedef typelist<int, char> list;

get<list, 0>::type //returns int

get<list, 1>::type //returns char

Since typelist is a plain list, get<> can be implemented as a constant time random access enumerator.

template<typename T> struct get<T, 0>

{

enum { index = 0 };

typedef typename T::type1 type;

};

template<typename T> struct get<T, 1>

{

enum { index = 1 };

typedef typename T::type2 type;

};

...

ttl::meta::type_switch

type_switch is a run-time switch-like function on typelists.

struct my_functor

{

template<typename T>

void operator()() { ...do something }

};



typedef typelist<int, char> list;

void f()

{

int type = 0;



my_functor f;

type_switch<list> ts;

ts(type); //calls my_functor::operator()<int>();

type = 1;

ts(type); //calls my_functor::operator()<char>();

type = 2;

ts(type); //run-time exception

}

func namespace contains:

The ttl::func::function is very similar to boost::function (see Sample).



Comments

I was scared by the complexity of the boost::function implementation for such a basic class. Just including "boost/function.hpp" (10 parameters limit) adds about 4 sec in compilation time on my machine while including "ttl/func/function.hpp" (the same limit) doesn't appear to have any noticeable impact on the compilation time.

named_params_function<> can be used to to convert any function into a functor with named parameters. The declaration consists of pairs (parameter name, parameter type). Here is an example.

#include "ttl/func/named_params_function.hpp" // there is a C function that is implemented somewhere. // int CreateWidget( const char* title, int style ); // define argument names // struct title; struct style; // declare a function that returns 'int' and accepts // named paramters 'title' and 'style' typedef ttl::func::named_params_function< int //the function returns 'int' ( //'argument name', 'argument type' title, const char* ,style, numeric_argument<int, 45> //the default is 45 ) > create_widget; int main() { create_widget cw(CreateWidget); //call with 'style' 3 and title 'nill' cw( cw.arg<style>(3) ); //call with 'title' "hi" and 'style' 45 cw( cw.arg<title>("hi") ); //call with 'title' "hi" and 'style' 5 //note the order of parameters doesn't matter only names cw( cw.arg<style>(5), cw.arg<title>("hi") ); return 0; };

create_widget<> can be used with any function or method that have the specified signature. You can find a working sample for class members in samples/test/named_params_test.cpp. The sample used boost::bind for bindind class members.

The var namespace contains

variant<>

apply_visitor<>

get<>

ttl::var::variant

ttl::var::variant has almost the same interface as boost::variant including unary and binary visitors. variant cannot include 'const' types. variant<>::list is the typelist<> class composed of of the variant types.



Comments



There are following differences between ttl variant and boost variant.

ttl::var::variant can be in an uninitialized (singular) state. ttl::var::variant becomes singular if it has not been initialized or there was an exception (see section on exceptions) during assignment. On the other hand boost::variant always contains a user defined type. If the assignment fails the content of boost::variant might be changed under the covers to a default-constrictable value. boost::variant visitors cannot assume that the variant content has not been changed by the variant internally. ttl::var::variant visitors simply won't be called if the variant is in singular state. In other words if a visitor is called, the variant content is guaranteed be valid (that was set by the user).

ttl::var::variant visitors are not required to be derived from another class like boost::variant visitors need to be derived from boost::variant::static_visitor.

ttl visitors cannot return a value. This limitation has a simple workaround with a wrapper class.

int visitor( my_type& x );



struct visitor_wrapper

{

int return_value;



void operator()( my_type& x )

{

return_value = visitor(x);

}

};



variant<my_type&> var;

visitor_wrapper vw;

apply_visitor(var, vw);

int r = vw.return_value;

We are planning to generalize such a wrapper latter.







In some cases boost::variant will allocate heap memory dynamically, ttl::var::variant never does that.

ttl::var::variant implementation is very simple.







The sig namspace contains:

signal

connection

ttl::sig::signal

tll::sig::signal provides a very similar functionality to boost::signal. Although ttl::sig::signal has a more basic interface comparing to boost::signal, we believe that ttl::sig::signal is a better choice for wide range of applications where the advanced features of boost::signal are not needed. For a sample code, see Sample



Comments

ttl::sig::signal calls connections in the order they created.

Comparison with boost::signal

ttl::sig::signal has a much better compilation time performance.

ttl::sig::signal has a very simple implementation that resides in header files only. There is no need for a dynamic or static libraries.

ttl::sig::signal supports plain functions directly while it appears that boost::signal requires functors (see Sample).

The following boost::signal features are not supported.

boost::signals groups.

boost::signals combiners.

trackable objects. So when a connected object is destroyed, all its connections must be implicitly closed.

boost::signals::scoped_connection

Note: we believe that all these features can be implemented using ttl::sig::signal as a low-level building block.













