Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behavior of __LINE__ when used in a macro

Why does __LINE__ evaluate differently based on whether it's used inside a function-like macro or a regular function?

For example:

#include<stdio.h>

#define A() printf("%d\n",__LINE__);

int main(void) {
/* 6 */  A();
/* 7 */  A(
/* 8 */    );
/* 9 */  printf("%d\n",__LINE__
/* 10 */  );
}

I would expect to get:

6
7
9

But instead we get (using clang-1000.10.44.4):

6
8
9

Note how in the function-like macro spread over lines 7 & 8, the last line occupied is used, rather than the first.

GCC's documentation is light on details: https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Why do I care? I'm writing a parser that needs to find the line numbers of all instances of the macro A in such a way as to line up with what __LINE__ will return. It is much harder to find the last line of the macro usage rather than the first due to the need to parse possibly escaped arguments.

like image 926
Chris Merck Avatar asked May 17 '19 20:05

Chris Merck


People also ask

What is the type of __ LINE __?

__LINE__ __LINE__ is a preprocessor macro that expands to current line number in the source file, as an integer. __LINE__ is useful when generating log statements, error messages intended for programmers, when throwing exceptions, or when writing debugging code.

What does the macro line do in C?

The __FILE__ macro expands to a string whose contents are the filename, surrounded by double quotation marks ( " " ). If you change the line number and filename, the compiler ignores the previous values and continues processing with the new values.

When would you use a macro function?

Macros are used for short operations and it avoids function call overhead. It can be used if any short operation is being done in program repeatedly. Function-like macros are very beneficial when the same block of code needs to be executed multiple times.

What is correct way to define macro?

A macro is a fragment of code that is given a name. You can define a macro in C using the #define preprocessor directive. Here's an example. #define c 299792458 // speed of light. Here, when we use c in our program, it is replaced with 299792458 .


2 Answers

The C implementation does not replace the A() macro until it sees the closing ). That ) appears on line 8, so that is the point at which macro replacement occurs.

The specifics of __LINE__ with regard to macro replacement are not well specified by the C standard. You should likely not rely on a particular behavior here. Certainly the C implementation cannot replace the A() macro while it has read only up to line 7, as it does not know what is coming yet. Once it has seen the closing ), then, as it replaces the macro, it might consider the replacement tokens to be occurring on line 7 or on line 8 or on some mix—the C standard is not specific about this; line numbers are largely irrelevant to C semantics at this point, and the __LINE__ macro is largely a convenience for debugging and other development work, not a feature for production programs (although it may have some uses for them).

In the printf, the C implementation recognizes the __LINE__ macro as soon as it sees the end of the line. (Actually, the parsing is more involved; the input has been tokenized, but the effect is the __LINE__ token is recognized when the end-of-line character is examined.) It is on line 9, so it is replaced by 9. The fact it is an argument to printf is irrelevant. The C implementation does not have the process the printf in order to replace the __LINE__ token that appears on line 9; they do not interact.

like image 111
Eric Postpischil Avatar answered Sep 28 '22 20:09

Eric Postpischil


After a few tests on my own, you can not rely on a behaviour here as even gcc changed its behaviour across versions.

#include <stdio.h>                                                                                                                                            
#define ShowLine() printf("__LINE__ evals to %d", __LINE__);                                                                                                  
int main()                                                                                                                                                    
{                                                                                                                                                             
    ShowLine(                                                                                                                                                
              );                                                                                                                                              
    return 0;                                                                                                                                                 
}

This code shows

  • 5 with gcc 11
  • 6 with gcc 7
  • 6 with clang13
like image 45
Charles G Avatar answered Sep 28 '22 19:09

Charles G