views:

124

answers:

5

I am using an aggregate initializer to set up a block of static data for a unit test.

I would like to use the array size as the expected number of elements, but this can fail if too few initializers are provided:

my_struct_type expected[14] =
{
    { 1.234, 0, 'c' },
    { 3.141, 1, 'z' },
    { 2.718, 0, 'a' }
};

This gives no compiler error in Visual Studio 2008.

I would like to be able to use it as such:

const unsigned expected_size = sizeof(expected) / sizeof(my_struct_type);

BOOST_CHECK_EQUAL(points.size(), expected_size);

for( int i = 0; i < expected_size; i++ )
{
    BOOST_CHECK_EQUAL(points[i].value, expected[i].value);
    BOOST_CHECK_EQUAL(points[i].count, expected[i].count);
    BOOST_CHECK_EQUAL(points[i].sym,   expected[i].sym);
}

but because I don't have a compile-time guarantee of 14 points, this runs off the end of the array end of the provided values and into the default-initialized values.

Can I somehow enforce the number of aggregate array initializers at compile-time?

A: 

According to the msdn, if fewer initializers are specified, the remaining elements are initialized with 0, so the code should work nonetheless.

Femaref
True, it will compile, but it won't have the semantics I'm looking for.
mskfisher
+4  A: 

First: There might be a warning for this. Have you tried compiling at the highest warning level?

Then: If you swap which value is calculated and which is literal, you could raise a compile-time error:

my_struct_type my_array[] = // <== note the empty []
{
    { 1.234, 0, 'c' },
    { 3.141, 1, 'z' },
    { 2.718, 0, 'a' }
};

BOOST_STATIC_ASSERT( sizeof(my_array)/sizeof(my_array[0]) == 14 );
sbi
I liked @Potatoswatter's answer for its novelty, but yours is the most pragmatic and applicable without touching my existing structures.
mskfisher
+4  A: 

Actually it won't run off the end of the array, because the compiler will default-initialize all the elements of the array that you didn't initialize yourself.

If you're trying to make sure that you have a specific number of configured initializers, I'm not sure how to do that.

If you just want to make sure the array is the number of items you have:

my_struct_type expected[] =
{
    { 1.234, 0, 'c' },
    { 3.141, 1, 'z' },
    { 2.718, 0, 'a' }
};

Will do the trick. Then just use sizeof(expected) / sizeof(expected[0]) to get the total number of array elements.

Mark B
Not zero-initialize, but use default initializer.
Vlad Lazarenko
@Vlad: I just saw your answer. I remember it calling for zero-initialization, but I guess not. So if you saw my other comment, nevermind :)
Dennis Zickefoose
@Vlad Lazarenko: Well, according to the latest C++ specification, the remaining elements will be subjected to *value-initialization*. In fact, there's no such thing as "default *initializer* " in C++. There's *default-initialization*, but that's what would happen in C++98. In C++03 though *value-initialization* takes place.
AndreyT
+1 for catching my poor "off the end of the array" prhasing.
mskfisher
+1  A: 

Just for the sake of a non-Boost answer…

You can add an initialization requirement by modifying my_struct_type.

template< typename T >
struct must_be_initialized {
    T value;

    must_be_initialized( T const &v ) : value( v ) {}
     // no default constructor!

    operator T& () { return value; }
    operator T const& () const { return value; }
};

struct my_struct_type {
    must_be_initialized< double > f;
    int i;
    char c;
};

my_struct_type expected[14] =
{
    { 1.234, 0, 'c' },
    { 3.141, 1, 'z' },
    { 2.718, 0, 'a' }
     // error: no default constructor exists
};

my_struct_type is still an aggregate, but it is not POD.

Potatoswatter
Interesting. It also looks like C++0x's uniform initialization would allow a private default constructor to accomplish this, as well. http://en.wikipedia.org/wiki/C%2B%2B0x#Uniform_initialization
mskfisher
@msk: or a deleted default constructor.
Potatoswatter
Even better, no unnecessary access specifiers.
mskfisher
So, whenever default constructor deletion is allowed in `COMPILER_OF_CHOICE`...
mskfisher
A: 

ISO/IEC 14882 (First edition 1998-09-01) in p. 8.5.1.7 states the following:

If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized (8.5). [Example: struct S { int a; char* b; int c; }; S ss = { 1, "asdf" }; initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0. ]

Simply, the answer to your question is no.

Vlad Lazarenko