tags:

views:

1005

answers:

8

If I code this

std::map<int, char> example = { (1,'a'),
                            (2, 'b'),
                            (3, 'c') };

then g++ says to me

deducing from brace-enclosed initializer list requires #include <initializer_list>
in C++98 ‘example’ must be initialized by constructor, not by ‘{...}’   

and that annoys me slightly because the constructor is run-time and can, theoretically fail.

Sure, if it does, it will fail quickly and ought to do so consistently, so that I ought to quickly locate & correct the problem.

But, still, I am curious - is there anyway to initialize map, vector, etc, at compile time?


Edit: I should have said that I am developing for embedded systems. Not all processors will have a C++0x compiler. The most popular probably will, but I don't want to encounter a gotcha & have to maintain 2 version s of the code.

As to Boost, I am undecided. They are wishy-washy on the use of their Finite State Machine classes in embedded systems, so that is actually what I am coding here, Event/State/Fsm classes.

Sigh, I guess I'd better just play it safe, but I hope that this discussion has been helpful for others.

+8  A: 

Not in C++98. C++0x supports this, so if you enable C++0x flags and include what g++ suggests, you can.

Artyom
Yes this has been a missing feature for very long. I really hope C++0x turns out to be C++OA so we can start demanding this from our compiler vendors.
daramarak
Hm, if i read it right, implementations *may* statically initialize non-aggregates too if it the semantics are not changed. If the static analysis can't guarantee that, C++0x initializer lists wouldn't change a thing.
Georg Fritzsche
@daramarak: I think the compiler vendors are the ones writing the standard ;v) , support should follow soon after the spec in major compilers whether you ask for it or not, and for minor compilers, good luck.
Potatoswatter
+7  A: 

You can use Boost.Assign library:

#include <boost/assign.hpp>
#include <map>
int main()
{
   std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');
}

However, as Neil and others pointed in the comments below, this initialization occurs in runtime, similarly to UncleBean's proposal.

mloskot
+1 for mentioning its possible
Viktor Sehr
Its not, `boost::assign` results in runtime initialization.
Georg Fritzsche
@Neil, @gf Ah, gotcha, I've missed the point it must not be runtime of course.
mloskot
+2  A: 

With C++0x you might need to use braces all the way (use the new-style syntax for each pair as well):

std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} };

Those brackets to construct pairs are not meaningful. Alternatively you can fully name out each pair or use make_pair (as you'd do in C++98)

std::map<int, char> example = {
    std::make_pair(1,'a'),
    std::make_pair(2, 'b'),
    std::make_pair(3, 'c')
};

As to creating those instances at compile-time: no. STL containers all encapsulate entirely runtime memory management.

I suppose, you'd only really have a compile-time map with libraries like boost's metaprogramming (not 100% sure, if it is entirely correct, and haven't studied what it could be good for):

using namespace boost::mpl;
map<
    pair<integral_c<int, 1>, integral_c<char, 'a'> >,
    pair<integral_c<int, 2>, integral_c<char, 'b'> >,
    pair<integral_c<int, 3>, integral_c<char, 'c'> >
> compile_time_map;
UncleBens
UncleBens, your second example doesn't compile, this approach IMO _has_ to be done like I posted in my answer.
Dmitry
@Dmitry: The second example assumes -std=C++0x as well (OP's compiler appears to support it, or it wouldn't be talking about initializer_list).
UncleBens
UncleBens, oh, ok, didn't know that, my gcc is a bit old, no C++0x for me yet.
Dmitry
+2  A: 

With pre-C++0x, the closest thing you can get is by not using containers designed for runtime usage (and limiting yourself to fundamental types and aggregates):

struct pair { int first; char second; };
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage

This could then be accessed using some kind of map view, or you could implement a wrapper that allows for aggregate initialization similar to what Boost.Array does.

Of course the question is wether the benefits justify the time put into implementing this.

If my reading is correct here, C++0x initializer-lists may give you static initialization of non-aggregates like std::map and std::pair, but only if that doesn't change semantics compared to dynamic initialization.
Thus, it seems to me you can only get what you asked for if your implementation can verify via static analysis that the behaviour doesn't change if the map is statically initialized, but no guarantees that it happens.

Georg Fritzsche
A: 

There is a trick you can use, but only if this data will not be used in any other static constructor. First define a simple class like this:

typedef void (*VoidFunc)();
class Initializer
{
  public:
    Initializer(const VoidFunc& pF)
    {
      pF();
    }
};


Then, use it like this:

std::map<std::string, int> numbers;
void __initNumsFunc()
{
  numbers["one"] = 1;
  numbers["two"] = 2;
  numbers["three"] = 3;
}
Initializer __initNums(&__initNumsFunc);


Of course this is a bit overkill so I would recommend using it only if you really have to.

Adis H
Double underscores are reserved for the compiler.
GMan
I think that's just another variation of the C++0x and boost.assign suggestions. There's nothing compile-time here, just another way to set values in a global map instance.
UncleBens
I used the underscores just to make it clear that you wouldn't want to touch the function or the object in the remaining code. (you could use an unnamed namespace too)And yes nothing compile-time about it but as I said, it's a trick - the data will be initialized before main()
Adis H
+7  A: 

It's not exactly static initialization, but still, give it a try. If your compiler doesn't support C++0x, I'd go for std::map's iteration constructor:

std::pair<int, std::string> map_data[] = {
    std::make_pair(1, "a"),
    std::make_pair(2, "b"),
    std::make_pair(3, "c")
};

std::map<int, std::string> my_map(map_data,
    map_data + sizeof map_data / sizeof map_data[0]);

This is pretty readable, doesn't require any extra libraries and should work in all compilers.

Dmitry
A: 

There is no standard way to initialize std::map at compile time. As others have mentioned, C++0x will allow the compiler to optimize the initialization to be static if possible, but that will never be guaranteed.

Remember, though, that the STL is just an interface spec. You can create your own compliant containers and give them static initialization capability.

Depending on whether you plan on upgrading your compiler and STL implementation (particularly on an embedded platform), you might even just dig into the implementation you're using, add derived classes, and use those!

Potatoswatter
A: 

@Dmitry

std::pair<int, std::string> map_data[] = {
    std::make_pair(1, "a"),
    std::make_pair(2, "b"),
    std::make_pair(3, "c")
};

std::map<int, std::string> my_map(map_data,
    map_data + sizeof map_data / sizeof map_data[0]);

How do you initialize both map_data and my_map in the constructor initialization list?

areddy