1

I am writing a logger and I would like the logger to also record which line, function, and file called it. I have tried #define my_logger(format, ...) _log(format, __LINE__, __PRETTY_FUNCTION__, __FILE__, __VA_ARGS__) which works however, this leads to several problems for me (I can't easily overload it, I can't put it in a namespace, not the most portable, etc).

Is there a way where I can declare a normal function like void my_logger(const char* format, ...) and in the function definition have it know the line it was called from with something equivilent to __LINE_FUNCTION_WAS_CALLED_FROM__ if such macro existed? At a minimum it needs to work on GCC and really should be cross-platform.

Edit: By cross-platform, I only mean Linux and Windows on x86 and ARM

Ebony Ayers
  • 350
  • 1
  • 3
  • 9
  • Related: https://stackoverflow.com/q/691719/10957435 –  Aug 15 '20 at 03:53
  • @Chipster I don't want to use stack tracing because I want the logger to have then information even in release builds and I don't want the overhead of stack tracing – Ebony Ayers Aug 15 '20 at 04:10
  • That makes sense. I didn't think that was exactly what you wanted, but I just figured I'd mention it since you should be able to accomplish the same thing that way if it was desirable. Of course, that way isn't portable across platforms anyway. –  Aug 15 '20 at 04:14
  • How many years of work can you afford spending on your project? – Basile Starynkevitch Aug 15 '20 at 04:26

2 Answers2

4

There cannot be such macro as __LINE_FUNCTION_WAS_CALLED_FROM__. There's no way it could work.

Since C++20, there is a way to get the line, function, etc. into a normal function by passing default initialised std::source_location:

void log(std::string_view message,
         std::source_location location = std::source_location::current());

However, since it relies on a defaulted parameter, variadic arguments are a bit tricky to implement: How to use source_location in a variadic template function?

Prior to C++20, a macro is your only portable option.


I see that you call a function named _log in your macro. That identifier is reserved to the language implementation in the global namespace. You probably should give the function another name to avoid undefined behaviour.

log is also reserved in the global namespace. You should declare all your names in a custom namespace anyway (except that custom namespace naturally).


[the macro is] ... not the most portable

If you were to replace __PRETTY_FUNCTION__ with __func__, then the macro would be portable to all standard conforming compilers - although the exact string given by __func__ may differ between implementations.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thank you for pointing out the problem with _log. I'm coming from python where that's standard. Is there anything else I should avoid of that nature? – Ebony Ayers Aug 15 '20 at 03:50
  • @EbonyAyers See the standard sections [lex.name] and [reserved.names]. By the way, `log` is also reserved in global namespace, so put it in a namespace of your own. – eerorika Aug 15 '20 at 03:51
  • 1
    This can be implemented on your own with compiler builtins [How could `std::experimental::source_location` be implemented?](https://stackoverflow.com/q/34405913/995714) – phuclv Aug 15 '20 at 03:55
  • 2
    @EbonyAyers [Here](https://stackoverflow.com/q/228783/10957435) is a list of some things you shouldn't do with underscores. –  Aug 15 '20 at 03:55
  • I'm not seeing where `_log` specifically is reserved, but it does start with an underscore, so that would mean it's reserved for file scope, yes? –  Aug 15 '20 at 04:03
  • 1
    @Chipster Yes, it is reserved in the global namespace only. – eerorika Aug 15 '20 at 04:05
  • 2
    @Chipster from 17.4.3.1.2 Global names [lib.global.names] "Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.165" – Ebony Ayers Aug 15 '20 at 04:07
  • 2
    @eerorika: *"it cannot be used in conjunction with variadic arguments."*, see [how-to-use-source-location-in-a-variadic-template-function](https://stackoverflow.com/questions/57547273/how-to-use-source-location-in-a-variadic-template-function). – Jarod42 Aug 15 '20 at 07:13
0

Is there a way where I can declare a normal function like void my_logger(const char* format, ...) and in the function definition have it know the line it was called from with something equivalent to __LINE_FUNCTION_WAS_CALLED_FROM__ if such macro existed?

Not in general.

On Linux x86-64, in 2020, with all your (or other) C++ code (C++11 or better) compiled with DWARF debug information (so if using a recent GCC, with g++ -g -Wall and if using a recent Clang, with clang++ -g -Wall), you might use Ian Taylor's libbacktrace library.

Also, a good optimizing compiler is allowed to inline expand or unroll loop (and actually g++ -O2 or clang++ -O2 will do both, and so does Microsoft compilers), and then your __LINE_FUNCTION_WAS_CALLED_FROM__ makes no sense.

As an open source example, RefPerSys is using it to show backtrace and part of call stacks. Disclaimer: RefPerSys is my own project.

Be aware of ASLR related to shared libraries. And plugins loaded with dlopen(3) add complexity to the picture.

At a minimum it needs to work on GCC and really should be cross-platform.

It cannot be cross-platform. Some architectures (e.g. IBM Z series) have different calling conventions, and you'll need efforts (or pay them) to port libbacktrace to them.

You might consider debugging on some systems, then porting your code to others, and you should consider using frameworks like libSFML or Qt for your game engine. My opinion is to recommend debugging your code on Linux (to make it correct) and later porting it to Windows.

If you need cross-compilation, things become harder. you may also consider metaprogramming approaches: writing your C++ code generator or using other ones (e.g. like SWIG or ANTLR)

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • As much as I love the Linux master race I am writing a game engine and unfortunately need to support windows. As part of that, I would like to support MSVC rather than locking windows builds to MinGW. I looked at stack tracing and would rather not use it as I would like to be able to have the logger track call locations even in build releases and I am not sure what performance penalty I would get from stack tracing. – Ebony Ayers Aug 15 '20 at 04:05
  • My recommendation: compile on Linux (with `g++ -Wall -Wextra -g`). Once your code is debugged, port it to Windows. **First make your code correct and bug free on *one* computer and OS.** Later spend efforts on optimization and porting it to other platforms. – Basile Starynkevitch Aug 15 '20 at 04:24