Static tables initialization in C++

Discussion in 'C++' started by Stefano Sabatini, Feb 1, 2008.

  1. 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.
    --
    Stefano Sabatini
    Linux user number 337176 (see http://counter.li.org)
    Stefano Sabatini, Feb 1, 2008
    #1
    1. Advertising

  2. Stefano Sabatini

    Jerry Coffin Guest

    In article <>,
    says...

    [ ... ]

    > 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.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Feb 1, 2008
    #2
    1. Advertising

  3. Stefano Sabatini

    Pete Becker Guest

    On 2008-02-01 09:13:18 -0500, Stefano Sabatini
    <> said:

    > 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;
    }

    --
    Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
    Standard C++ Library Extensions: a Tutorial and Reference
    (www.petebecker.com/tr1book)
    Pete Becker, Feb 1, 2008
    #3
  4. On 2008-02-01, Jerry Coffin <> wrote:
    > In article <>,
    > says...
    >
    > [ ... ]
    >
    >> 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.
    --
    Stefano Sabatini
    Linux user number 337176 (see http://counter.li.org)
    Stefano Sabatini, Feb 1, 2008
    #4
  5. On 2008-02-01, Pete Becker <> wrote:
    > On 2008-02-01 09:13:18 -0500, Stefano Sabatini
    ><> said:
    >
    >> 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.
    --
    Stefano Sabatini
    Linux user number 337176 (see http://counter.li.org)
    Stefano Sabatini, Feb 1, 2008
    #5
  6. Stefano Sabatini

    Pete Becker Guest

    On 2008-02-01 12:00:52 -0500, Stefano Sabatini
    <> said:

    > On 2008-02-01, Pete Becker <> wrote:
    >>
    >> #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.

    --
    Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
    Standard C++ Library Extensions: a Tutorial and Reference
    (www.petebecker.com/tr1book)
    Pete Becker, Feb 1, 2008
    #6
  7. Stefano Sabatini

    Bo Persson Guest

    Stefano Sabatini wrote:
    > On 2008-02-01, Jerry Coffin <> wrote:
    >> In article <>,
    >> says...
    >>
    >> [ ... ]
    >>
    >>> 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
    Bo Persson, Feb 2, 2008
    #7
  8. Stefano Sabatini

    Jerry Coffin Guest

    In article <>,
    says...
    > On 2008-02-01, Jerry Coffin <> 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.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Feb 3, 2008
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JKop
    Replies:
    10
    Views:
    940
  2. Matthias Kaeppler
    Replies:
    2
    Views:
    439
    Victor Bazarov
    Jul 18, 2005
  3. Replies:
    6
    Views:
    458
    Ron Natalie
    Dec 11, 2005
  4. toton
    Replies:
    5
    Views:
    932
    Victor Bazarov
    Sep 28, 2006
  5. Jess
    Replies:
    23
    Views:
    926
Loading...

Share This Page