3

I would like to define a macro to concat __func__ (or __FUNCTION__) with __LINE__:

The following works fine:

// macro_test.cc
#include <iostream>

#define STR2(X) #X
#define STR(X) STR2(X)
#define FILE_LOCATION __FILE__ ":" STR(__LINE__) " "

int main() {
  std::cout << FILE_LOCATION << "is <file_name>:<line_number>" << std::endl;
  return 0;
}

And here is the output

$ ./a.out 
macro_test.cc:8 is <file_name>:<line_number>

However the following gives a compilation error (I just replaced __FILE__ with __func__):

// macro_test.cc
#include <iostream>

#define STR2(X) #X
#define STR(X) STR2(X)
#define FUNC_LOCATION __func__ ":" STR(__LINE__) " "

int main() {
  std::cout << FUNC_LOCATION << "is <function_name>:<line_number>" << std::endl;
  return 0;
}

~$ gcc macro_test.cc 
macro_test.cc: In function ‘int main()’:
macro_test.cc:5:32: error: expected ‘;’ before string constant
 #define FUNC_LOCATION __func__ ":" STR(__LINE__) " "
                                ^
macro_test.cc:8:16: note: in expansion of macro ‘FUNC_LOCATION’
   std::cout << FUNC_LOCATION << "is <function_name>:<line_number>" << std::endl;

Does anyone know the reason for this and how can I achieve this?

I am using gcc 5.4.0 on Linux (Ubuntu 18.04).

alk
  • 69,737
  • 10
  • 105
  • 255
Curious
  • 2,783
  • 3
  • 29
  • 45
  • `STR(__func__)` ? – bolov May 01 '20 at 14:20
  • @bolov with `STR(__func__)` the output is: `__func__:8 is :` – Curious May 01 '20 at 14:23
  • 2
    `#define FUNC_LOCATION [](auto fn, auto ln) { std::stringstream ss; ss << fn << ":" << ln << " "; return ss.str(); }(__func__, __LINE__)` – Eljay May 01 '20 at 14:29
  • @Eljay what about to say that in an answer ? the other answer say why the OP proposal does not work but does not answer to OP question "how to do" – bruno May 01 '20 at 15:07

2 Answers2

7

gives a compilation error [...] anyone know the reason for this

__func__ is a variable:

static const char __func__[] = "function-name";

It is not to a (string) literal (to which for example __FILE__ "expands".)

(docs are here: https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html)

alk
  • 69,737
  • 10
  • 105
  • 255
1

Instead of trying to stitch together incompatible types into a single string, you could have an immediately invoked function expression (borrowing from JavaScript terminology) as the macro implementation.

Since it is being immediately executed, I pass in the two preprocessor identifiers as parameters.

They shouldn't be baked into the body of the lambda because then the __func__ will reflect the lambda rather than the routine invoking the lambda.

#include <sstream>
#define FUNC_LOCATION \
    [](auto fn, auto ln) { \
        std::stringstream ss;
        ss << fn << ":" << ln << " "; \
        return ss.str(); \
    }(__func__, __LINE__)

int main() {
    std::cout << FILE_LOCATION << "is <file_name>:<line_number>" << std::endl;
    return 0;
}
Eljay
  • 4,648
  • 3
  • 16
  • 27
  • This would work but I guess there would be a runtime cost associated with it,. I was looking for a pure macro that is evaluated at compile time (preprocessor) and so there is no runtime cost. Guess that is not possible. – Curious May 02 '20 at 08:17
  • 1
    @Curious • there is another cost by making C string literals, which is they cannot be consolidated because they differ. In my project, we were able to reduce the size of the executable by 50 MB by getting rid of our FILE+LINE concatenation and instead having two parameters, similar to what I've provided. These days 50 MB may not seem like much, but for a downloadable iOS or Android app that is still a big footprint for no appreciable benefit. (Without _profiling_ to prove that the performance loss is significant, I wouldn't put any stock in "runtime cost" claims.) – Eljay May 02 '20 at 16:16