1

I have some pre-processor definitions that make storing UI text easy in a single array (see below). Also makes supporting other languages less cumbersome.

#define DECLARE_STRING_ENUM_FST(name, value) name
#define DECLARE_STRING_ENUM_SND(name, value) value
#define DECLARE_STRING_ENUM(name, macro) \
    typedef enum name { macro(DECLARE_STRING_ENUM_FST) } name; \
    static const char* name##_sztable[] = { macro(DECLARE_STRING_ENUM_SND) }; \

// this is a string table usage 
#define MSG_ENUM_(X) \
    X(STR_ONE, "One"), \
    X(STR_TWO, "Two"), \
    X(STR_THREE, "Three"), \
    X(STR_PRESS_ENTER, "Press Enter")

// the actual declaration is here
DECLARE_STRING_ENUM(menu, MSG_ENUM_)

the result is an array of strings and an enum representing indexes in the array.

However, since it is an array of pointers to constant char*, it takes up ram which is very scarce on this device. The couple of large string tables in the program are taking up ~30% of the available RAM. so this can't be ignored.

Dependency of RAM would go to zero if the enum values were starting positions of the null-terminated sub-strings in one large const char string stored in code space.

i.e:

menu_sztable[] = "One\0Two\0Three\0Press Enter\0";
STR_ONE -> 0
STR_TWO -> 4
STR_Three -> 8
STR_PressEnter -> 14

Is there a clever way to use the C-Preprocessor to make this? I'd like to avoid building up the enum manually or having to to write a pre-build command program that converts the arrays.

6
  • 1
    Isn't this more or less what the compiler does for you with all string literals? Commented Oct 9, 2012 at 17:22
  • The strings could be embedded into a packed struct and the enums taken with offsetof directive. It's then a linker issue to put the struct to .text segment. Commented Oct 9, 2012 at 17:25
  • @AkiSuihkonen can you provide an example? That sounds promising. Commented Oct 9, 2012 at 17:27
  • For easily automating things like this, why try to make due with the C preprocessor? Write a simple python script that generates your perfectly efficient C code. That's easy, clean, easily translated across languages. Just add your python script as a step in your build process. Commented Oct 9, 2012 at 17:34
  • 1
    Have you tried static const char* const name##_sztable[] = /* ... */ which should put the array of pointers into ROM with most embedded device tool chains (assuming that ROM/flash space isn't an issue as well)? Commented Oct 9, 2012 at 18:00

4 Answers 4

2

If you make your array of pointers const most (all?) tool chains will place that array into ROM/flash which usually has less of an issue with space:

static const char* const name##_sztable[] = /* ... */
//                 ^^^^^
Sign up to request clarification or add additional context in comments.

Comments

1

It's not a full solution, maybe a halfway ...

#include <stdlib.h>
#include <stddef.h>
static struct foo {
    char arr0[5];
    char arr1[4];
    char arr2[4];
} tmp = { "Zero","One","Two"};
enum {
   ARR0 = offsetof(struct foo, arr0),
   ARR1 = offsetof(struct foo, arr1),
   ARR2 = offsetof(struct foo, arr2)
};

int main()
{
      printf("%d %d %d\n",ARR0,ARR1,ARR2);
      return 0;
}

Can't remember the linker option, but when disassembling this, I'll get:

    .file   "tst.c"
    .data     // manually replace with .text
_tmp:
    .ascii "Zero\0"
    .ascii "One\0"
    .ascii "Two\0"  // manually insert .data here, recompile and run...
    .def    ___main;        .scl    2;      .type   32;     .endef

1 Comment

thanks. It's a start. Perhaps I could use the sizeof for the array sizes and preprocessor to spit this section out. I should be able to force .readonly with the const keyword.
0

You could first define a whole bunch of compile time constants that will hold the postion of your strings, something like

#define DEFINE_LENGTH(NAME, STR) NAME ## _POS,       \
      NAME ## _DUMMY = (NAME ## _POS + sizeof(STR))

inside the declaration of an enumeration

enum {
  ... your macro expansion goes here ...
};

Then you create a long string by concatenating

#define DEFINE_COMPONENT(NAME, STR) STR "\0"

inside the initialization of the long string

char const table[] = ... your macro expansion goes here ...;

Now your string pointers are are obtained with

#define DEFINE_VARIABLE(NAME, STR) *const NAME = &table[NAME ## _POS]

inside the following setting:

char const  ... your macro expansion goes here ...;

(maybe there are still some syntax errors here and there but I hope you get the picture.)

All that would be simpler if you'd use a macro meta-package such as boost or P99.

Comments

0

Why not do:

#if ENGLISH
#define STR_ONE "one"
#define STR_TWO "two"
...
#elif SPANISH
#define STR_ONE "uno"
#define STR_TWO "dos"
...
#endif

Is there a reason why you need offsets and not string pointers themselves?

1 Comment

Yes, lists. e.g. as slow medium fast, or January, February, etc. You can index jump to the right string.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.