Question Details

No question body available.

Tags

c++ constants

Answers (4)

Accepted Answer Available
Accepted Answer
April 30, 2025 Score: 2 Rep: 44,810 Quality: High Completeness: 80%

I'm trying to ensure that the tags and values don't get out of sync with one another, if someone forgets to add a value in one but not the other place.

"Idiot-proofing" code like that would be tricky if the strings were defined in a different place than the tags. This is a good use of X macros, like:

// All entries are managed here:

#define TAGENUMDATA(F) \ F(TAG1, "val1") \ F(TAG2, "val2")

// Macro-expanded, do not touch:

#define TAGENUMENUMERATOR(tag, str) tag, #define TAGENUMSTRING(tag, str) str,

enum { // expands to: TAG1, TAG2, TAGENUMDATA(TAGENUMENUMERATOR) NUMTAGS };

constexpr const char vals_ok[] = { // expands to "val1", "val2", TAG_ENUM_DATA(TAG_ENUM_STRING) };

constexpr const char vals[] = { // expands to "val1", "val2", TAGENUMDATA(TAGENUMSTRING) // one extra entry corresponding to NUMTAGS nullptr };

When someone wants to add a new entry or modify an existing one, they would just be modifying the TAGENUMDATA macro; the enum and array are generated from that data.


Note that modern, idiomatic C++ should probably use std::array instead of plain arrays, std::stringview instead of const char*, and enum class instead of enum. I assume you have good reasons to avoid these things, seeing that this is embedded.

April 29, 2025 Score: 7 Rep: 4,768 Quality: Medium Completeness: 70%

As suggested in comments, std::array should make this easier to write:

constexpr std::array vals = []()
{
    std::array rval{};
    rval[TAG2] = "val2";
    rval[TAG1] = "val1";
    return rval;
}();

On godbolt

I'm not sure if this really ensures that your values and names don't get out of sync, but you can add a staticassert that you didn't miss any, like staticassert(!std::ranges::contains(vals, nullptr)); (example on godbolt; example when you forgot a tag).

April 29, 2025 Score: 1 Rep: 58,111 Quality: Low Completeness: 50%

For an embedded system you can use an array of struct so that the data can be placed into the constant data section of your application.

struct Record
{
    enum tags tagid;
    char * text;
};

IMHO, a better record would have a fixed width text field.

For example:

const Record enumtable[] =
{
    {tag1, "val1"},
    {tag2, "val2"},
    //...
};

The actual array allows you to place the data into a read-only section, and access the data directly. Other data structures may need to be constructed at runtime.

April 30, 2025 Score: 1 Rep: 5,247 Quality: Low Completeness: 80%

It is possible to define some map class usable for constexpr objects. It is then possible to define your mapping in a natural way as follows

constexpr CTMap enum_table
{{{
    {tags::TAG2, "val2" },
    {tags::TAG1, "val1" }
}}};

where CTMap is defined by

template 
struct CTMap
{
    using KV = std::pair;
    std::array pairs;

constexpr Value at (Key key) const { for (int i=0; i