Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / C++
Article

Pretty Good Initialization Library

Rate me:
Please Sign up or sign in to vote.
5.00/5 (28 votes)
25 Nov 20046 min read 111.2K   1.2K   44   13
Are you tired of filling data manually into STL containers? With the Initialization Library it gets a lot easier.

Preface

The author reminds the reader that a more in depth description of this library and its implementation appeared in the October 2003 issue of C/C++ Users Journal in the article "Non-intrusive Container Initialization". The part featured in this article has been refined and is waiting for a review at boost (see http://www.boost.org/). The other part about enumerating ranges was not featured in the article. Having a templated conversion operator can still be tricky for some compilers, but Leor Zolman has managed to port it to a wide range of compilers in his own initilization library (see http://www.bdsoft.com/tools/initutil.html ). Please note that the forwarding mechanism can be greatly simplified by calling container.insert( container.end(), new_value ) ).

September 25th: A new superior library that incorporates most of the ideas presented in this article has now been releasedas part of boost 1.32 (see www.boost.org). The library is called boost.assign and can be found at http://www.boost.org/libs/assign/doc/index.html

Contents

Introduction

Are you tired of filling data manually into STL containers? With the Initialization Library it gets a lot easier. One can simply say

vector<int>  primes;
set_cont( v ) += 1, 2, 3, 5, 7, 11;
     
list<int>  ten_odd = enum_n( 10, 2 ); 
// '{ 1,3,5,7,9,11,13,15,17,19 }'     

map<const char*,int>  months;  
set_map( months ) += "january",   31, "february", 28,
                     "march",     31, "april",    30,
                     "may",       31, "june",     30,
                     "july",      31, "august",   31,
                     "september", 30, "october",  31,
                     "november",  30, "december", 31;

class Widget;
deque<Widget> dw;
set_cont( dw ) += Widget( 2, 2 ), Widget( "name", 2 );

The key idea is to overload the operator,() in a non-intrusive and convenient manner. Some convenient functions can be used to create enumerated or generated data.

Motivation

The reason that this library exist can be summarized as follows:

Quick and easy initialization of STL containers.

If one needs to fill STL containers with constant data, it is a somewhat tedious work. This need might be frequent in learning, testing and prototyping situations. In other languages the job is often easy. For example, in Perl one can say

@date  = (8, 24, 70);   
%fruit = ('apples', 3, 'oranges', 6); 

to assign to an array or map, respectively. The array syntax is almost identical in C++, but since built-in arrays are not particularly handy, one will usually stuff data into an STL container [1]. In C++ one can rely on two approaches [2]:

  • use a temporary array
  • use a member function of the container

The first approach is for instance

const int N = 6;
const char* a[N] = { "isomer", "ephemeral", "prosaic", 
                    "nugatory", "artichoke", "serif" };
set<const char*>c A( a, a + N );

which requires manual book-keeping of the array size. The latter approach is even worse:

vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
...

Clearly one would be better off with a syntax similar to that of Perl. Moreover, it would be nice if it worked for containers of arbitrary types and not just containers of built-in types.

Synopsis

We shall first present the interface and then discuss implementations details. Template and namespace parameters on return types has been hidden to keep the synopsis easier to use; the curious can simply look in init.hpp The interface is quite small:

namespace init
{    
    
    template< typename Container >
    <A href="#comma">Comma_initializer</A>      <A href="#set_cont">set_cont</A>( Container& c );

    template< typename Container >
    <A href="#map_comma">Map_comma_initializer</A>  <A href="#set_map">set_map</A>( Container& c );

    //////////////////////////////////////////////
    // <A href="#enum">enums</A> 
    //////////////////////////////////////////////
      
    template< typename T >
    <A href="#any">Any_container</A>  
    <A href="#enum_n">enum_n</A>( size_t n );

    template< typename T >
    Any_container  
    <A href="#enum_n2">enum_n</A>( size_t n, T step_size );

    template< typename T >
    Any_container  
    <A href="#enum_n_from">enum_n_from</A>( size_t n, T from );

    template< typename T >
    Any_container  
    <A href="#enum_n_from2">enum_n_from</A>( size_t n, T from, T step_size );

    template< typename T, typename Functor >
    Any_container  
    <A href="#n_generated">n_generated_by</A>( T n, Functor generator );

} // namespace 'init'

Comma_initializer and Map_comma_initializer are classes that wrap the normal member functions like push_back() and insert() in a call to operator,(). In this manner the interface will be the same for all containers. Overloading operator comma is sometimes viewed as a bad practice [3]. However, it has been done with success in eg. the Generative Matrix Computation Library and Blitz to initialize matrices (see [4]) and [5]). The Initialization Library overloads the comma operator in a safe manner by letting <A href="#set_cont">set_cont</A> and <A href="#set_map">set_map</A> return an object that is responsible for the initialization. Therefore it takes explicit action from the programmer to begin using the overloaded operator,().

All the enum() functions are meant for generating sequences of numeric types. This means built-in types, complex, <A href="http://www.boost.org/libs/rational/index.html">boost::rational</A> or types with similar behavior. The enum() functions come all come in two flavors: one that enumerate numbers with an interval of 1 and one that allows the user to specify the interval size.

Any_container is a class that converts implicitly to all the containers supported by the library. The table below also shows what member function the overloaded comma operator forwards to:

Standard container

 
vector push_back()
list push_back()
deque push_back()
set insert()
multisetinsert()
map insert()
multimapinsert()
stack push()
queue push()
priority_queuepush()
 

SGI container extensions

slist insert()
hash_set insert()
hash_multisetinsert()
hash_map insert()
hash_multimapinsert()
 

Boost containers

array Not available

An explanation for each function follows.

  • Comma_initializer set_cont( Container& c );

    set_cont is short for "set container". Most of the functionality of the library is available through this function. Using it requires two steps: create the container and call the function on the container:

    vector<float> vf;
    set_cont( vf ) += 1.f, 3.f, 5.f;
    set_cont( vf )  = 1.f;

    The difference between using operator+=() and operator=() is the same as with built-in types. The former adds to the container whereas the latter clears the container before adding new elements.

    It is worth noticing that the template code allows one to initialize a container of any type provided that the type meets the common requirements of copy-construction and copy-assignable. One will simply call constructors directly and initialize the container with anonymous objects.

    One can also use containers that are not currently supported if they have a push_back() member function. The reason for this is that push_back() is the default insertion policy and specializations have been written for all other containers. Otherwise, one must make a partial specialization of the Insert_policy class for one's own container.

  • Map_comma_initializer set_map( Container& c );

    The function provides the same interface as set_cont() except it is meant for map classes. Using it is a little different since one needs to specify key-data pairs:

    map<string,int> m;
    set_map( m ) += "fish", 1, string( "horse" ), 2;
    set_map( m )  = "cow", 1;

    As before, operator=() resets the containers first. Again, the code will work with arbitrary types that fulfill the requirements of the container. If one makes an error like

    set_map( m ) += "fish", "fish", 2, 3;

    an assertion will trigger at run time because the key-data alternation was wrong.

    If one needs to use a custom map container, it will work immediately if the map class supports insert( const value_type& ) where value_type is a pair< const key,data > ). Otherwise, one needs to make a partial specialization of Map_insert_policy to support one's custom container.

  • Any_container enum_n( size_t n );
    Any_container enum_n( size_t n, T step_size );

    enum_n is short for "enumerate n elements". The first container will hold the n elements

    { 1, ..., n } 

    and the second will hold the n elements

    { 1 + 0*step_size, ..., 1 + (n-1)*step_size } .

    In the one-argument version, one must explicitly specify the type of the generated values as a template parameter.

    One can generate reverse sequences by specifying a negative step_size.

    Example:

    vector<int> vi = enum_n<int>( 3 );
    // 1,2,3
    assert( v.size() == 3 );
    vector<float> vf = enum_n<float>( 5 );
    
    stack<double> sd = enum_n( 4, 2.1 );
    // 1.0, 3.1, 5.2, 7.3
    assert( sd.top() == 7.3 );
               
    boost::array<int,3> a = enum_n( 3, -1 );
    // 1, 0, -1
  • Any_container enum_n_from( size_t n, T from );
    Any_container enum_n_from( size_t n, T from, T step_size );

    enum_n_from is short for "enumerate n elements starting from". The first container will hold the n elements

    { from + 0, ..., from + (n-1) } 

    whereas the second will hold the n elements

    { from + 0*step_size, ...,from + (n-1)*step_size }

    One does not need to specify a template parameter since the contained type will be deduced from the second parameter.

    One can generate reverse sequences by specifying a negative step_size.

    Example:

    list<int> = enum_n_from( 4, 4 );
    // 4,5,6,7
    
    queue<complex> qc = enum_n_from( 2, 
            complex( 2, 0 ), complex( 1 , 2 ) );
    // (2,0), (3,2), (4,4)
    
    vector<int> vi = enum_n_from( 5, 5, -1 );
    // 5, 4, 3, 2, 1
  • Any_container <BR>n_generated_by( T n, Functor generator );

    Call the generator n times and return the results in a container. The function simply wraps the std::generate algorithm. This means that

    vector<int> v = n_generated_by( 10, some_functor() );

    is equivalent with

    vector<int> v( 10 );
    generate( v.begin(), v.end(), some_functor() );

    and that the requirements for using the algorithm remain same. In particular, the generator must return type T and take no arguments. Note that the client is required to supply the contained type as the type of n. For example,

    vector<float> f = n_generated_by( 10.f, &rand ); . 

Acknowledgement

The idea for an initialization library is not new . The functionality of the library resembles Leor Zolman's STL Container Initialization Library a great deal. Leor Zolman has also contributed with helpful feedback.

Download

  • init.hpp - main header ( will change into several the near future )
  • tst.cpp - test program ( won't compile alone, download some other of Leor Zolman's utilities here )

References

  1. The boost array class does make arrays more convenient
  2. http://www.bdsoft.com/tools/initutil.html
  3. Scott. Meyers, "More Effective C++", Item 7, Addison Wesley, 1996
  4. K. Czarnecki and U.W. Eisenecker, "Generative programming", Addison-Wesley, 2000
  5. http://www.oonumerics.org/blitz/

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Denmark Denmark
Thorsten Ottosen holds a Bsc in Computer Science at Aalborg University, Denmark. After having studied mathematics at University of Technology in Sydney, Australia, he has now returned to Denmark to write a second thesis in the area of decision support systems. His first thesis was in computer graphics since he used to dream about making computer games.

Thorsten is also a co-owner and part-time employee of Dezide, a company that specializes is trouble-shooting programs based on Bayesian-network technology.

In his spare-time he codes/reads/hacks C++ and participates in ANSI/ISO C++ committee meetings. In his spare-time of his spare-time he enjoys jogging, reading, and being with family and friends

Comments and Discussions

 
GeneralThis is great- however I still need to make it work Pin
Guidolavespa11-Mar-09 13:05
Guidolavespa11-Mar-09 13:05 
General:) Pin
Goran Mitrovic8-Dec-04 21:06
Goran Mitrovic8-Dec-04 21:06 
GeneralI never thought I'd see a good use of the comma operator Pin
Don Clugston7-Dec-04 17:40
Don Clugston7-Dec-04 17:40 
GeneralRe: I never thought I'd see a good use of the comma operator Pin
Thorsten Ottosen8-Dec-04 0:03
Thorsten Ottosen8-Dec-04 0:03 
Generaluse a temporary array Pin
gknowles7-Jul-04 8:55
gknowles7-Jul-04 8:55 
GeneralRe: use a temporary array Pin
Thorsten Ottosen19-Aug-04 3:06
Thorsten Ottosen19-Aug-04 3:06 
General! Pin
Cap'n Code16-Mar-04 19:28
Cap'n Code16-Mar-04 19:28 
GeneralRe: ! Pin
蒋刚15-Feb-14 2:18
蒋刚15-Feb-14 2:18 
Questioneasier input implementation? Pin
seriema10-Mar-04 21:40
seriema10-Mar-04 21:40 
AnswerRe: easier input implementation? Pin
Thorsten Ottosen11-Mar-04 0:56
Thorsten Ottosen11-Mar-04 0:56 
GeneralLooks familiar Pin
Nemanja Trifunovic4-Mar-04 9:27
Nemanja Trifunovic4-Mar-04 9:27 
Have you seen this post in the Lounge[^]?

Anyway, great job. You have my 5.
GeneralRe: Looks familiar Pin
Kippesoep4-Mar-04 11:42
Kippesoep4-Mar-04 11:42 
GeneralRe: Looks familiar Pin
Nemanja Trifunovic4-Mar-04 20:03
Nemanja Trifunovic4-Mar-04 20:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.