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.
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.
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"
//real C++ version is already taken care of
#if
defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
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.
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.
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.
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.
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<<"\n";
}
void
operator()( double x )
{
std::cout<<"double
event: "<<x<<"\n";
}
};
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<> 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; } };
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 ); }
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
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.
The meta namespace provides a small set of meta programming facilities.
typlist<>
get<>
type_switch<>
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.
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;
};
...
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 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
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.