1
#include <stdio.h>

void print(int a = __LINE__){printf("hello %d\n", a);}

void main(){
  print();
  print();
  print();
  print();
}

The __LINE__ macro in this case expands to 3, so the print function is called 4 times with the same value. Is there a way to convince the compiler to expand this macro at the callsite, so that the print function would be called with 6,7,8,9 instead of 3,3,3,3 with features that exist in C++11?

My usecase:

In my application, I provide multiple functions that take a unique ID. The ID should be unique per callsite/location (so if the function is called two times throught the same statement, it should receive the same id). Currently, the user always has to manually type a LOCATION macro at the callsite, like this:

#define S1(x) #x //voodoo to concat __FILE__ and __LINE__
#define S2(x) S1(x)
#define LOCATION __FILE__ S2(__LINE__)

do_stuff1(arguments, LOCATION)
do_stuff2(arguments, LOCATION)

It would be more convenient if I could save them the typing, without creating a macro for each function like this:

#define do_stuff1(do_stuff1_imp(arguments, LOCATION))
#define do_stuff2(do_stuff2_imp(arguments, LOCATION))

Hence I thought a default argument would do the trick. Is there any way to achieve this?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
pulp_user
  • 2,454
  • 3
  • 14
  • 18
  • 3
    Do the work in a macro, `#define print() {printf("hello %d\n", __LINE__);}`. – Eljay Nov 20 '18 at 18:16
  • This is exactly what I wrote in the last code block, only translated on `do_stuff` instead of `print`. I am aware of this solution and wanted to know if there was a less tedious way to do this (I have quite a lot of these functions. I would need to make a macro for each of them). – pulp_user Nov 20 '18 at 18:19
  • Wait, no, sorry I was wrong. Your way makes the whole function a macro. That is indeed different. I will consider that, thanks! – pulp_user Nov 20 '18 at 18:22
  • 1
    @MatthieuBrucher Don't change the tags from C++ to C like that. Even if all of the code in the question would compile with a C compiler, that doesn't make the question a C question (note that default arguments are a C++ feature). The OP is using a C++ compiler and is looking for C++ answers, not C. – Justin Nov 20 '18 at 18:28

3 Answers3

5

You seem to be looking for std::experimental::source_location, which will be std::source_location in a future standard:

#include <experimental/source_location>

void print(std::experimental::source_location const& location
    = std::experimental::source_location())
{
    printf("hello %s:%d\n", location.file_name(), location.line());
}
Justin
  • 24,288
  • 12
  • 92
  • 142
  • 2
    This is indeed exactly what I was looking for. I will not be able to use C++17 though (which this requires I think?). Sorry, I forgot to mention this in the question. I will keep this in mind for the future though. Thanks! – pulp_user Nov 20 '18 at 18:24
  • 1
    @pulp_user It doesn't require C++17, it requires that your standard library / compiler implements at least this part of the library fundamentals TS v2. You can try it and see if it works, else look at the documentation for your compiler / standard library to see if it works – Justin Nov 20 '18 at 18:25
3

The __LINE__ macro in this case expands to 3

Sure because your function declaration is done at line 3 and the prepreprocessor expands it from there.

Another macro instead of a function declaration would do the trick well (taken from @Ejay's comment):

#define print() printf("hello %d\n", __LINE__)

See a working example here.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

You can write a macro which inserts the __FILE__ and __LINE__ into the argument list:

// Note: this doesn't work with a 0-argument function. Supporting 0-arg functions
// is much harder to do; I'd recommend just having a separate macro like as follows
#define LOCATED(fn, ...) fn(__VA_ARGS__, __FILE__, __LINE__)

#define LOCATED0(fn) fn(__FILE__, __LINE__)

void print(char const* file_name, int line)
{
    printf("hello %s:%d\n", file_name, line);
}

int do_something(int value, char const* file_name, int line);

int main() {
    LOCATED0(print);
    LOCATED(do_something, 42);
}

With a little more work, you can make it look nicer at the call-site:

#define LOCATED(...) LOCATED_##__VA_ARGS__

void print(char const* file_name, int line)
{
    printf("hello %s:%d\n", file_name, line);
}
#define LOCATED_print() ::print(__FILE__, __LINE__)

int do_something(int value, char const* file_name, int line);
#define LOCATED_do_something(value) ::do_something(value, __FILE__, __LINE__)

int main() {
    LOCATED(print());
    LOCATED(do_something(42));
}
Justin
  • 24,288
  • 12
  • 92
  • 142
  • I fail to see how this is different from `print(LOCATION)` (how I currently do it in my code) besides turning it inside out. – pulp_user Nov 20 '18 at 18:40
  • 1
    @pulp_user You are right that it's not better than `print(LOCATION)`, but it's an alternative – Justin Nov 20 '18 at 18:48