Static string literal table?
Asked Answered
P

4

13

What is the correct way in C++ to create a global & static table of strings?

By "global", I mean: Useable from any file that includes the header. But not part of some run-time created singelton objcet.

By "static", I mean: As little run time set up possable. Data in read only memory pages. Only 1 instance of data per app.

By "string", I mean: Null terminated array of chars is fine. std::string would be nice, but I don't think it can be done in terms of the above. Correct?

By "table", I mean: I mean an indexable array. So I guess not a table per-se. But I'm flexable on this point. Open to ideas.

By "C++", I mean: C++ not C. (Update: C++98, not C++11)

Ppm answered 8/1, 2013 at 4:53 Comment(5)
What do you mean by table?Ranzini
Global array? const char *table[SIZE];Impostume
I don't think C vs C++ would make much of a difference here given that std::string is out of the question...Amon
What do you mean by "to"?Biel
Could use an enum to represent each string constant, then use an efficient technique like codereview.stackexchange.com/a/14315 to convert the enum to a string. Uses an array to hold the strings.Haw
B
10

strings.h

extern const char* table[];

strings.cpp

const char* table[] = {
    "Stack",
    "Overflow",
}

Another take on this, using error codes for a lookup table:

err.h

#define ERR_NOT_FOUND    0x1004
#define ERR_INVALID      0x1005

bool get_err_msg(int code, const char* &msg);

err.cpp

typedef struct {
    int errcode;
    const char* msg;
} errmsg_t;

static errmsg_t errmsg_table[] = {
    {ERR_NOT_FOUND, "Not found"},
    {ERR_INVALID,   "Invalid"}
};

#define ERRMSG_TABLE_LEN  sizeof(errmsg_table)/sizeof(errmsg_table[0])

bool get_err_msg(int code, const char* &msg){
    msg = NULL;
    for (int i=0; i<ERRMSG_TABLE_LEN; i++) {
        if (errmsg_table[i].errcode == code) {
            msg = errmsg_table[i].msg;
            return true;
        }
    }
    return false;
}

main.cpp

#include <stdio.h>
#include "err.h"

int main(int argc, char** argv) {
    const char* msg;
    int code = ERR_INVALID;
    if (get_err_msg(code, msg)) {
        printf("%d: %s\n", code, msg);
    }
    return 0;
}

I'm sure there is a more C++ way of doing this, but I'm really a C programmer.

Beecham answered 8/1, 2013 at 4:58 Comment(1)
Because it will be included by other C files?Beecham
B
6

Use a std::array of string literals. It has no constructor so it will be loaded statically in the .rodata section like a C array, yet it has a standard C++ library interface. (iterators, size, etc)

A.h:

#include <array>

extern std::array<const char*, 3> A;

A.cpp:

std::array<const char*, 3> A = { "foo", "bar", "baz" };

http://en.cppreference.com/w/cpp/container/array

Bethesda answered 8/1, 2013 at 4:58 Comment(3)
It's a pity that this requires the element count to be repeated, but worth it I think for the std::array niceness.Ellga
A char const* is not too nice though (in terms of interface). Depending on the C++11-ness of the compiler, I believe it would be possible to built a class with constexpr constructor on the model of array_ref such as proposed here.Imaginary
Is there a C++98 way to do this? (Aside from what @WhozCraig suggested)Ppm
P
4

I like Jonathon Reinhart way, I always do it that way especially if we have a structure of elements,

however, it requires a loop to find the elements (Not indexed), so if you like an improvement, especially for embedded systems style.

enum ERR_INDEX{
ERR_NOT_FOUND=0,
ERR_INVALID,
ERR_BAD_LENGTH,
ERR_MORE_ERR1,
ERR_MORE_ERR2,
ERR_MORE_ERR3,
};

static const char * errmsg_table[] = {
    "Not found",
    "Invalid",
    "bad message length",
    "error 1",
    "error 2",
    "error 3",
};

int main(int argc, char** argv) {
    int code = ERR_INVALID;
    printf("%d: %s\n", code, errmsg_table[code]);
    printf("%d: %s\n", code, errmsg_table[ERR_BAD_LENGTH]);

return 0;
}
Pike answered 3/5, 2013 at 18:42 Comment(2)
this is good for error codes that start at 0 and increment by one.Pike
if you have a more error code that not in sequence, you can have a dictionary that translate error code to sequential list, or just have a dictionary that has the error code as a key, and error message as a string :)Pike
T
3

The table_n provided strictly to ensure you at least have a hint how big it is:

Table.h

// header file
extern const size_t table_n;
extern const char* table[];

Table.cpp

// c/cpp file
const char *table[] = 
{
     "one",
     "two",
     "there"
};

const size_t table_n = sizeof(table)/sizeof(table[0]);

Or something like that.

Tamarin answered 8/1, 2013 at 5:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.