Click here to Skip to main content
Click here to Skip to main content

Pretty Good Initialization Library

, 25 Nov 2004
Rate this:
Please Sign up or sign in to vote.
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 set_cont and set_map 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, boost::rational 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:

<!--
  • graph based on adjacency_list
  • graph based on adjacency_matrix -->
  • Standard container

     
    vector push_back()
    list push_back()
    deque push_back()
    set insert()
    multiset insert()
    map insert()
    multimap insert()
    stack push()
    queue push()
    priority_queue push()
     

    SGI container extensions

    slist insert()
    hash_set insert()
    hash_multiset insert()
    hash_map insert()
    hash_multimap insert()
     

    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 renum_n_from( size_t n, T from );
      Any_container  
          renum_n_from( size_t n, T from, 
      
              &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp T step_size );


      renum_n_from is short for "reversely enumerate n elements starting from". The first container holds the n elements
      { from, from - 1, ..., from - (n-1) }
      and the second container holds the n elements
      { from - 0*step_size, ..., from - (n-1)*step_size } .
      does not need to supply a template parameter since the type deduction happens from the second argument.

      Example: <xmp> deque di = renum_n_from( 4, 10, 2 ); deque di2 = enum_n_from( 4, 10, -2 ); // 10, 8, 6, 4 assert( di == di2 ); -->
    • Any_container
      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 ); . 
    <!--

    Examples

    • Partial specialisation of a custom made container
    • Filling a container with random numbers
    • Filling a container with generated numbers
    • Filling a container with custom made objects

    -->

    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 )
    <!--

    See Also

    -->

    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

    Share

    About the Author

    Thorsten Ottosen
    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 PinmemberGuidolavespa11-Mar-09 13:05 
    General:) PinmemberGoran Mitrovic8-Dec-04 21:06 
    GeneralI never thought I'd see a good use of the comma operator PinmemberDon Clugston7-Dec-04 17:40 
    GeneralRe: I never thought I'd see a good use of the comma operator PinmemberThorsten Ottosen8-Dec-04 0:03 
    Generaluse a temporary array Pinmembergknowles7-Jul-04 8:55 
    GeneralRe: use a temporary array PinmemberThorsten Ottosen19-Aug-04 3:06 
    General! [modified] PinmemberCap'n Code16-Mar-04 19:28 
    GeneralRe: ! Pinmember蒋刚15-Feb-14 2:18 
    Questioneasier input implementation? Pinmemberseriema10-Mar-04 21:40 
    AnswerRe: easier input implementation? PinmemberThorsten Ottosen11-Mar-04 0:56 
    GeneralLooks familiar PinmemberNemanja Trifunovic4-Mar-04 9:27 
    GeneralRe: Looks familiar PinmemberKippesoep4-Mar-04 11:42 
    GeneralRe: Looks familiar PinmemberNemanja Trifunovic4-Mar-04 20:03 

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

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

    | Advertise | Privacy | Mobile
    Web03 | 2.8.140821.2 | Last Updated 26 Nov 2004
    Article Copyright 2004 by Thorsten Ottosen
    Everything else Copyright © CodeProject, 1999-2014
    Terms of Service
    Layout: fixed | fluid