3

Essentially, I want to do this:

#include "foo.h"
#include "bar.h"
static const unsigned line_after_includes = __LINE__;

int main()
{
    foo(line_after_includes);
    bar(line_after_includes);
    return 0;
}

Except like this:

#include "foo.h"
#include "bar.h"
#define LINE_AFTER_INCLUDES __LINE__

int main()
{
    FOO(LINE_AFTER_INCLUDES);
    BAR(LINE_AFTER_INCLUDES);
    return 0;
}

Is it possible to make LINE_AFTER_INCLUDES expand to the value of __LINE__ on the line on which LINE_AFTER_INCLUDES was defined, so I can use it with other macros later? I would just use a variable like in the first code snippet, but I need the value as an integer literal, because I will be using it in a switch statement. The alternative is to do

    #define LINE_AFTER_INCLUDES 3 // MAKE SURE THIS IS ON LINE IT'S DEFINED AS!!!

which is ugly and harder to maintain. I would settle for that, but I will be doing something a bit more complex than this example...

Mets
  • 75
  • 2
  • 8

1 Answers1

4

The best way to do this is given in @Bathsheba's answer here: https://stackoverflow.com/a/24551912/1366431

typedef char LINE_AFTER_INCLUDES[__LINE__];

You can then access the value of __LINE__ at that declaration by calling sizeof(LINE_AFTER_INCLUDES), because its value has been fixed into part of the newly-declared array type. sizeof is a C-level compile-time constant, which works for switch and related things (no division is necessary: the size of char is guaranteed to be 1, as it's the unit of measurement). The only disadvantage of this is that it's C-level rather than preprocessor-level, so it can't be used with #if or for token-pasting.


(original, which I spent ages typing:)

So the problem here is that macro definitions are lazy; i.e. that macro invocations aren't expanded until they're actually needed to be inserted into some kind of output. Because of this, __LINE__'s expansion is delayed until it's too late.

Luckily, there's a way to force eager evaluation in the preprocessor - the macro only needs to be demanded by some kind of output - not necessarily program body text. Remember that macros can also be expanded to form the arguments to preprocessor directives - and that preprocessor directives are then - having been controlled by the forced expansion - able to create further definitions. This handy observation is the basis of Boost's "evaluated slots" functionality, which uses it to provide things like eager evaluation and mutable preprocessor variable slots.

Unfortunately you can't use Boost slots with __LINE__, as it suffers from the same problem - but we can steal the idea to produce two rather inelegant solutions. They're each ugly in their own special ways.

Option 1:

Use a shell script to produce a large number (a few hundred?) of include-able files with the following naming scheme and contents:

// linedefs/_42_.h
#define LINE_AFTER_INCLUDES 42

...then use as follows:

#define GEN_LINE_INC(L) _GEN_LINE_S(_GEN_LINE_(L))
#define _GEN_LINE_(L) linedefs/_##L##_.h
#define _GEN_LINE_S(S) _GEN_LINE_S2(S)
#define _GEN_LINE_S2(S) #S

#include "foo.h"
#include "bar.h"

#include GEN_LINE_INC(__LINE__)

int main()
{
    FOO(LINE_AFTER_INCLUDES);
    BAR(LINE_AFTER_INCLUDES);
    return 0;
}

In other words, use the #include directive to force expansion of a macro which converts __LINE__ into a filename; including that filename produces the right value for the constant. This is inelegant because it requires a load of extra files and an external tool to generate them, but it's very simple.

Option 2:

Insert a large and ugly block into your main file below the #include section:

#include "foo.h"
#include "bar.h"

// <- This line is the one we generate the number for
#define LINE_BIT_0 0
#define LINE_BIT_1 0
#define LINE_BIT_2 0
#define LINE_BIT_3 0
#define LINE_BIT_4 0
#define LINE_BIT_5 0
#if (__LINE__ - 7) & 1
#  undef LINE_BIT_0
#  define LINE_BIT_0 1
#endif
#if (__LINE__ - 11) >> 1 & 1
#  undef LINE_BIT_1
#  define LINE_BIT_1 (1 << 1)
#endif
#if (__LINE__ - 15) >> 2 & 1
#  undef LINE_BIT_2
#  define LINE_BIT_2 (1 << 2)
#endif
#if (__LINE__ - 19) >> 3 & 1
#  undef LINE_BIT_3
#  define LINE_BIT_3 (1 << 3)
#endif
#if (__LINE__ - 23) >> 4 & 1
#  undef LINE_BIT_4
#  define LINE_BIT_4 (1 << 4)
#endif
#if (__LINE__ - 27) >> 5 & 1
#  undef LINE_BIT_5
#  define LINE_BIT_5 (1 << 5)
#endif
#define LINE_AFTER_INCLUDES (LINE_BIT_0 | LINE_BIT_1 | LINE_BIT_2 | LINE_BIT_3 | LINE_BIT_4 | LINE_BIT_5)


int main() ...

This version uses the #if directive to force expansion of the __LINE__ macro and convert it into bitflags, which are then recombined at the end. This is highly inelegant because it relies on precomputing the distance between each #if and the top of the block, since __LINE__ evaluates to different values in the course of the block; and it can't be factored out and hidden in a separate file, or else __LINE__ wouldn't work. Still, it works, and it doesn't require an external tool.

(In the event you have a huge number of #include lines, extending it to more than 6 bits should be straightforward.)

On the other hand, this sounds like an X/Y problem to me. There has to be an alternative to __LINE__ that would work better for this. If you're counting the number of #included files, perhaps you could use something like a line incrementing Boost.Counter at the end of each one? That way, you also wouldn't be vulnerable to formatting changes (e.g. blank lines in the #include section).

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • "this sounds like an X/Y problem to me" As I hinted at the end of the question, counting includes is just a minimal example. I'm actually trying to implement coroutines as syntax sugar using macros (just for fun - no one else will have to maintain this). – Mets Jul 24 '14 at 23:04
  • Shouldn't there be double quotes in `_GEN_LINE_`? – Mets Jul 24 '14 at 23:05
  • Awesome. Playing with macros is its own reward! And no, the output of `_GEN_LINE_` is passed to `_GEN_LINE_S` to be stringified there. (What I can't guarantee, off the top of my head, is whether this will *definitely* not insert spaces between e.g. the `_` and the `.`. But it works with GCC.) – Alex Celeste Jul 24 '14 at 23:14
  • 1
    By complete chance, there's also a *far* superior method here: http://stackoverflow.com/a/24551912/1366431 - please, use that. – Alex Celeste Jul 25 '14 at 15:49
  • You should add that to the beginning of your answer, but the other options are still useful if someone wants to use the value in an `#if`, since the preprocessor can't evaluate `sizeof`. – Mets Jul 25 '14 at 17:20