Static tables initialization in C++

  • Thread starter Stefano Sabatini
  • Start date
S

Stefano Sabatini

Hi guys,

the following simple C program is compiled with no problems:

#include <stdlib.h>
#include <stdio.h>

enum ColorId { green, blue, red, yellow, colorNb };

typedef struct Color {
enum ColorId id;
char *str;
} Color;

static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}
};

int main() {
int i;

printf ("%d\n", colorNb);
for (i=0; i< colorNb; i++)
printf("%s\n", Colors.str);
return 0;
}

It creates a static table Colors containing a limited number of Color
objects, which are then accessed through array subscripting using the
enum values.

I would like to implement a similiar thing in C++, and the nearest
thing I could achieve is this:

#include <iostream>
#include <string>

using namespace std;

enum ColorId { green, blue, red, yellow, colorNb };

class Color {
ColorId id;
string str;

Color(ColorId _id, string _str) { id= _id; str= _str; }
};

static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}
};

ostream &operator<<(ostream &str, const Color& color) {
string type;
str << color.str;
}

int main() {
for (int i=0; i< colorNb; i++)
cout << Colors << endl;

return 0;
}

but I'm getting these errors:
g++ -I/home/stefano/include/ Colors.cpp -o Colors
Colors.cpp:18: error: expected primary-expression before ‘[’ token
Colors.cpp:18: error: expected primary-expression before ‘{’ token
Colors.cpp:18: error: expected `}' before ‘{’ token
Colors.cpp:18: error: expected ‘,’ or ‘;’ before ‘{’ token
Colors.cpp:18: error: expected unqualified-id before ‘,’ token
Colors.cpp:19: error: expected unqualified-id before ‘[’ token
Colors.cpp:19: error: expected unqualified-id before ‘,’ token
Colors.cpp:20: error: expected unqualified-id before ‘[’ token
Colors.cpp:20: error: expected unqualified-id before ‘,’ token
Colors.cpp:21: error: expected unqualified-id before ‘[’ token
Colors.cpp:22: error: expected declaration before ‘}’ token
make: *** [Colors] Error 1

It seems the [enum val] = notation isn't unrecognized.

So my question is: how can I initialize in C++ a static table putting
object in a predefined position?

Is the above requirement (the creation of a static table containing
all the colors definition, that is containing all the definitions of
the Color object which will be used in the program) a meaningful one
or there are better mechanisms to achieve the same result in C++?

Many thanks in advance for any insight.

Regards.
 
J

Jerry Coffin

[ ... ]
class Color {
ColorId id;
string str;

Color(ColorId _id, string _str) { id= _id; str= _str; }

It doesn't make any real difference to what you're discussing, but the
generally preferred form would be:

Color(ColorID _id, string _str) : id(_id), str(_str) {}

There are also enough rules restricting the use of leading underscored
in your own code that many people advise against using them at all.
static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}

As you've guessed below, C++ doesn't support this syntax.
It seems the [enum val] = notation isn't unrecognized.

So my question is: how can I initialize in C++ a static table putting
object in a predefined position?

Is the above requirement (the creation of a static table containing
all the colors definition, that is containing all the definitions of
the Color object which will be used in the program) a meaningful one
or there are better mechanisms to achieve the same result in C++?

"Better" is really a question of values. Under the circumstances, your
values are contiguous, so a definition like:

static Color Colors[] = {
{ green, "green"},
{ blue, "blue"},
{ red, "red" },
{ yellow, "yellow"}
};

works perfectly well (i.e. produces the correct result). The compiler
will accept it, which most consider a really good thing. In theory, at
some point in the future you might want to use some non-contiguous
values, for which this is not nearly as well suited.

If it's certain, or even extremely likely, that you're dealing with a
relatively sparse array, you can easily use a map. This would be useful
if (for example) the color numbers were really RGB values for those
colors:

enum color_values {
red = 0xff0000,
green = 0x00ff00,
blue = 0x0000ff,
yellow = 0xffff00
};

static Color map_init[] = {
// same as Colors[] above
};

#define elements(array) (sizeof(array)/(sizeof(array[0])))

std::map<unsigned long, std::string>
Colors(map_init, map_init+elements(map_init));

You can use array syntax to insert new names as well:

unsigned long cyan = 0x00ffff;
unsigned long magenta = 0xff00ff;

Colors[cyan] = "Cyan";
Colors[magenta] = "Magenta";

Looking up existing colors _can_ be done the same way, with the proviso
that if you try to look up something that hasn't been inserted yet, this
will create an empty Color object and insert it at that point -- IOW, it
works fine if you know everything you look up has been inserted, but can
be a problem if you're trying to figure out whether a value has a name
or not. In that kind of situation, you'll generally want to use the
map's find function instead.
 
P

Pete Becker

Hi guys,

the following simple C program is compiled with no problems:

#include <stdlib.h>
#include <stdio.h>

enum ColorId { green, blue, red, yellow, colorNb };

typedef struct Color {
enum ColorId id;
char *str;
} Color;

static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}
};

int main() {
int i;

printf ("%d\n", colorNb);
for (i=0; i< colorNb; i++)
printf("%s\n", Colors.str);
return 0;
}

It creates a static table Colors containing a limited number of Color
objects, which are then accessed through array subscripting using the
enum values.


Seems overly elaborate, given the stated requirements.

#include <stdlib.h>
#include <stdio.h>

enum ColorID = { green, blue, red, yellow, colorNb };

static const char *Colors[] =
{
"green",
"blue",
"red",
"yellow"
}

int main()
{
int i;

printf("%d\n", colorNb);
for (i = 0; i < colorNb; ++i)
printf("%s\n", Colors);
return 0;
}
 
S

Stefano Sabatini

[ ... ]
class Color {
ColorId id;
string str;

Color(ColorId _id, string _str) { id= _id; str= _str; }

It doesn't make any real difference to what you're discussing, but the
generally preferred form would be:

Color(ColorID _id, string _str) : id(_id), str(_str) {}

There are also enough rules restricting the use of leading underscored
in your own code that many people advise against using them at all.

Mmh.., OK (well another convenction encourages to use m_Foo for object
(Member) fields).

Please could you make an example of a rule restricting the use of
underscored variables in such a context?
static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}

As you've guessed below, C++ doesn't support this syntax.
It seems the [enum val] = notation isn't unrecognized.

So my question is: how can I initialize in C++ a static table putting
object in a predefined position?

Is the above requirement (the creation of a static table containing
all the colors definition, that is containing all the definitions of
the Color object which will be used in the program) a meaningful one
or there are better mechanisms to achieve the same result in C++?

"Better" is really a question of values. Under the circumstances, your
values are contiguous, so a definition like:

static Color Colors[] = {
{ green, "green"},
{ blue, "blue"},
{ red, "red" },
{ yellow, "yellow"}
};

works perfectly well (i.e. produces the correct result). The compiler
will accept it, which most consider a really good thing. In theory, at
some point in the future you might want to use some non-contiguous
values, for which this is not nearly as well suited.

Tested it, and while the corresponding C contruct is accepted by my
compiler, it doesn't do it with the above construct.

That is while:
typedef struct Color {
enum ColorId id;
char *str;
} Color;


static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}
};

with the corresponding C++ code:

class Color {
public:
ColorId id;
string str;

Color(ColorId _id, string _str) : id(_id), str(_str) {}
};

static Color map_init[] = {
{ green, "green"},
{ blue, "blue"},
{ red, "red" },
{ yellow, "yellow"}
};

it complains saying:
make Colors
g++ -I/home/stefano/include/ Colors.cpp -o Colors
Colors.cpp:23: error: braces around scalar initializer for type ‘Color’
If it's certain, or even extremely likely, that you're dealing with a
relatively sparse array, you can easily use a map. This would be useful
if (for example) the color numbers were really RGB values for those
colors:

enum color_values {
red = 0xff0000,
green = 0x00ff00,
blue = 0x0000ff,
yellow = 0xffff00
};

static Color map_init[] = {
// same as Colors[] above
};

#define elements(array) (sizeof(array)/(sizeof(array[0])))

std::map<unsigned long, std::string>
Colors(map_init, map_init+elements(map_init));

You can use array syntax to insert new names as well:

unsigned long cyan = 0x00ffff;
unsigned long magenta = 0xff00ff;

Colors[cyan] = "Cyan";
Colors[magenta] = "Magenta";

Looking up existing colors _can_ be done the same way, with the proviso
that if you try to look up something that hasn't been inserted yet, this
will create an empty Color object and insert it at that point -- IOW, it
works fine if you know everything you look up has been inserted, but can
be a problem if you're trying to figure out whether a value has a name
or not. In that kind of situation, you'll generally want to use the
map's find function instead.

Makes sense, I definitively have to check for the map template. Still
I have to figure out how to tackle the initialization problem.

Many thanks for your detailed explanation.

Regards.
 
S

Stefano Sabatini

Hi guys,

the following simple C program is compiled with no problems:

#include <stdlib.h>
#include <stdio.h>

enum ColorId { green, blue, red, yellow, colorNb };

typedef struct Color {
enum ColorId id;
char *str;
} Color;

static Color Colors[] = {
[green] = { green, "green"},
[blue] = { blue, "blue"},
[red] = { red, "red" },
[yellow] = { yellow, "yellow"}
};

int main() {
int i;

printf ("%d\n", colorNb);
for (i=0; i< colorNb; i++)
printf("%s\n", Colors.str);
return 0;
}

It creates a static table Colors containing a limited number of Color
objects, which are then accessed through array subscripting using the
enum values.


Seems overly elaborate, given the stated requirements.

#include <stdlib.h>
#include <stdio.h>

enum ColorID = { green, blue, red, yellow, colorNb };

static const char *Colors[] =
{
"green",
"blue",
"red",
"yellow"
}

int main()
{
int i;

printf("%d\n", colorNb);
for (i = 0; i < colorNb; ++i)
printf("%s\n", Colors);
return 0;
}


Yes you're right, but this is a problem which I encounter quite
frequently, so I liked to get a general solution.

Splitting the color data in two different tables isn't safe, and I was
looking for something more OOP consistent, so that you can
treat the type as an object, so that you can do things like:

cout << "the type of the object " << obj << " is " << obj.type << endl;

Regards.
 
P

Pete Becker

#include <stdlib.h>
#include <stdio.h>

enum ColorID = { green, blue, red, yellow, colorNb };

static const char *Colors[] =
{
"green",
"blue",
"red",
"yellow"
}

int main()
{
int i;

printf("%d\n", colorNb);
for (i = 0; i < colorNb; ++i)
printf("%s\n", Colors);
return 0;
}


Yes you're right, but this is a problem which I encounter quite
frequently, so I liked to get a general solution.

Splitting the color data in two different tables isn't safe,


I didn't suggest two tables. Just one.
and I was
looking for something more OOP consistent, so that you can
treat the type as an object, so that you can do things like:

cout << "the type of the object " << obj << " is " << obj.type << endl;

There are only two situations where you need the name of the type: when
you're reading text, and when you're writing text. Other operations on
that type don't care about the names, so the names don't need to be
carried around with those objects. The names are redundant information:
the enum itself is sufficient.

To insert the name of an object, you'd either write

std::cout << Colors[whatever] << '\n';

or you'd write an inserter for the enum itself:

template <class Elem, class Tr = std::char_traits<Elem> >
ostream<Elem, Tr>& operator<<(ostream<Elem, Tr>& out, colorId color)
{
out << Colors[id];
return out;
}

and now you can say:

std::cout << id << '\n';

C++ is a multi-paradigm language. Object-oriented rigor is often
useful, but easily overdone.
 
B

Bo Persson

Stefano said:
[ ... ]
class Color {
ColorId id;
string str;

Color(ColorId _id, string _str) { id= _id; str= _str; }

It doesn't make any real difference to what you're discussing, but
the generally preferred form would be:

Color(ColorID _id, string _str) : id(_id), str(_str) {}

There are also enough rules restricting the use of leading
underscored in your own code that many people advise against using
them at all.

Mmh.., OK (well another convenction encourages to use m_Foo for
object (Member) fields).

Please could you make an example of a rule restricting the use of
underscored variables in such a context?

There is no restriction for the use of leading underscore in this
context. However, if we are to use the same names in different
scopes, we could just as well write it like this:

Color(ColorID id, string str) : id(id), str(str) {}


The argument against using leading underscore in member names, is that
when I see a name _id in a member function, it could be either a class
member or a global name used by the implementation. How would I know
which one it is?


Bo Persson
 
J

Jerry Coffin

On 2008-02-01, Jerry Coffin <[email protected]> wrote:

[ ... ]
Mmh.., OK (well another convenction encourages to use m_Foo for object
(Member) fields).

Please could you make an example of a rule restricting the use of
underscored variables in such a context?

That's an embedded underscore, not a leading underscore -- there's no
problem with embedded underscores (as long as you don't use two of them
in a row).

[ ... ]
static Color Colors[] = {
{ green, "green"},
{ blue, "blue"},
{ red, "red" },
{ yellow, "yellow"}
};

works perfectly well (i.e. produces the correct result). The compiler
will accept it, which most consider a really good thing. In theory, at
some point in the future you might want to use some non-contiguous
values, for which this is not nearly as well suited.

Tested it, and while the corresponding C contruct is accepted by my
compiler, it doesn't do it with the above construct.

Oops -- sorry 'bout that. This syntax works for C-like structs. When you
have a ctor involved, you can do something like this:

static Color Colors[] = {
Color(green, "green"),
Color(blue, "blue"),
Color(red, "red"),
Color(yellow, "yellow")
};

[ ... ]
#define elements(array) (sizeof(array)/(sizeof(array[0])))

I meant to mention that while this works under the circumstances, there
are other (template-based) methods that are a safer. This will fail
(produce bad results) if you accidentally use it on a pointer instead of
an array.
Makes sense, I definitively have to check for the map template. Still
I have to figure out how to tackle the initialization problem.

Unless there's a serious reason to do otherwise, I'd advise putting the
data into an external file, and just loading it from there.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top