4

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).

4
  • STR(__func__) ? Commented May 1, 2020 at 14:20
  • @bolov with STR(__func__) the output is: __func__:8 is <function_name>:<line_number> Commented May 1, 2020 at 14:23
  • 2
    #define FUNC_LOCATION [](auto fn, auto ln) { std::stringstream ss; ss << fn << ":" << ln << " "; return ss.str(); }(__func__, __LINE__) Commented May 1, 2020 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" Commented May 1, 2020 at 15:07

3 Answers 3

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)

Sign up to request clarification or add additional context in comments.

Comments

3

If your compiler supports c++20 (I tested on GCC 11 and above), you could use std::source_location instead of the macro magic.

It can be easily piped into a std::cout or std::clog. One logging function could look like the following (link to godbolt):

#include <source_location>
#include <iostream>

void log(const std::source_location location =
               std::source_location::current())
{
    std::cout << location.function_name() << ":" << location.line() <<
                 " is <file_name>:<line_number>" << std::endl;
};

int main() {
    log();    // line 12
    return 0;
}

Possible output:

int main():12 is <file_name>:<line_number>

1 Comment

Though this is a better technique, it requires C++20 and it is not clear from the question that this is available to the OP. Though future users may appreciate it.
2

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;
}

2 Comments

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 • 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.)

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.