6

I want to use the C preprocessor to count the amount of lines between two code locations. The basic idea is something like this:

#define START __LINE__ static char* string_list[] = { "some string", "another string", ... "last string" }; #define END __LINE__ #if END - START > 42 #error Too many entries #endif 

Of course this doesn't work because in this case START and END are merely redefinitions of the __LINE__ macro.
I was playing around a bit with the # and ## operators, but I could not get the preprocessor to evaluate START and END while the preprocessor is running.

My question is: is this possible at all?

Checking the size of the array during runtime is not an option.
Thanks for any hints or ideas in advance!

2
  • Advise static const char* string_list[]... Commented Jul 3, 2014 at 10:58
  • “Checking the size of the array during runtime”—that's impossible in C. Are you aware that sizeof yields a compile-time constant? And is “while the preprocessor is running” really a requirement (what sizeof wouldn't fulfill)? Commented Jul 3, 2014 at 11:30

3 Answers 3

8

You shouldn't use those macros for that purpose: the code will become completely unmaintainable if you introduce an extra line somewhere. And what if there are too few lines?

Instead of macros, use a static assert:

static_assert(sizeof(string_list) / sizeof(*string_list) == SOME_CONSTANT, "Wrong number of entries in string list."); 

If you aren't using C11 which has support for static_assert, you can write such an assert yourself, for example like this:

#define COMPILE_TIME_ASSERT(expr) {typedef uint8_t COMP_TIME_ASSERT[(expr) ? 1 : 0];} 
Sign up to request clarification or add additional context in comments.

8 Comments

+1. That COMPILE_TIME_ASSERT macro is cuter than the one I've been using for the last googleplex years.
@Bathsheba There's certainly a lot of such ugly macros out there :) Personally I think the standardized static_assert is reason enough for upgrading to C11.
Thank you very much. This seems like a reasonable way of accomplishing what I want
I would suggest to have the array be of size -1 instead of 0 to make the assertion fail. Just tested with GCC 4.9.0 only with -std=c99 and no other special switches specified and the code compiles fine with above macro as is. Array size of 0 only breaks if -pedantic is set. Array size of -1 does break with or without the switch so it makes it more reliable.
@PuerNoctis Test with a compliant C compiler instead. GCC is not a compliant C compiler unless you compile with std=c99 -pedantic-errors. GCC has a non-standard extension that allows arrays with size zero. This is not allowed by the C standard. See this
|
8

[Acknowledge @Lundin who points out that you can use a typedef]

This is one way

typedef uint8_t START[__LINE__]; /*put this on one line*/ typedef uint8_t END[__LINE__]; /*put this on another line*/ 

sizeof(END) - sizeof(START) is a compile-time expression giving the line offset.

Put in macros and rename START and END to taste.

4 Comments

Or use typedef uint8_t n1[__LINE__]; so that you don't have to actually allocate anything. Still, this doesn't solve the core problem, which is that __LINE__ macros are unsuitable for static checking of array sizes.
@Lundin: that's elegant and works for me (gcc and msvc2012). I've amended.
@Lundin, you mentioned "the core problem, which is that __LINE__ macros are unsuitable for static checking of array sizes." Enums are suitable for checking array, variable, and type sizes at compile-time. I'll post an elegant enum-based answer here next week, where I can call this: COMPILE_TIME_PRINT_SIZEOF(My_struct); to get this compile-time output, showing the My_struct size as 24 bytes: error: case value ‘24’ not in enumerated type ‘enum This_is_the_size_of_your_type_e’ [-Werror=switch].
0

If you declare the array thus:

static const char* string_list[42] = {... 

The compiler will tell you if you provide too many initialisers. You can provide a sentinel entry to mark the end if necessary:

static char* string_list[43] = { ..., NULL } ; 

And perhaps write a function to determine the length at runtime if you need to know that.

2 Comments

The problem is that this doesn't protect you against too few initializers.
Of course not, but the code in the question was never testing for too few.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.