0

I am trying to build a simple c++ logger for learning purposes, but I seem to be stuck at the following issue. Let's say that I have a namespace that contains two functions as follows:

namespace l {
    void _p(const char* file, const int line, int vl, const char* fmt, ...) {
        printf("%d:%s:%d: ", vl, file, line);
        va_list __args;
        va_start(__args, fmt);
        vfprintf(stdout, fmt, __args);
        printf("\n");
        va_end(__args);
    }
    void _p(const char* file, const int line, const char* fmt, ...) {
        printf("%s:%d: ", file, line);
        va_list __args;
        va_start(__args, fmt);
        vfprintf(stdout, fmt, __args);
        printf("\n");
        va_end(__args);
    }
}

and the main function is as follows:

int main(int argc, char** argv)
{
    l::_p(__FILE__, __LINE__, 12, "%s", "Hello World!");
    l::_p(__FILE__, __LINE__, "%s", "Hello World!");
    return 0;
}

what I would like to have is a macro such that when the user types: l::p(12, "%s", "Hello World!"); it would be replaced by l::_p(__FILE__, __LINE__, 12, "%s", "Hello World!"); at compile time. Similarly when the user types l::p("%s", "Hello World!"); it would be replaced by l::_p(__FILE__, __LINE__, "%s", "Hello World!"); at compile time

asdf
  • 460
  • 1
  • 10
  • 31
  • `vfprintf(stdout, fmt, __args);` is invalid when `fmt` is `NULL`. – KamilCuk Jun 02 '20 at 08:36
  • edited to remove `fmt=NULL` – asdf Jun 02 '20 at 08:39
  • Variadic functions are both burdensome and dangerous, nothing good comes out of them. Consider either taking a single struct/class as input, or to implement a number of overloaded functions corresponding to the accepted formats instead. – Lundin Jun 02 '20 at 08:43

2 Answers2

0

Use a logging macro

#define LOG(...) l::_p(__FILE__, __LINE__, __VA_ARGS__)

void f(int i) {
    LOG(12, "%d", i);
    LOG("%d", i);
}
Mestkon
  • 3,532
  • 7
  • 18
0

what I would like to have is a macro such that when the user types: l::p(12, "%s", "Hello World!"); it would be replaced by l::_p(__FILE__, __LINE__, 12, "%s", "Hello World!"); at compile time.

Sure, you can do:

#define p(...)  _p(__FILE__, __LINE__, __VA_ARGS__)

Macros are processed at preprocessing stage at which point compiler is not aware about anything complicated like namespace. So such a macro can be confusing to unaware programmers that will later try to define a function named p or call a function pointer named p.

Preferably in C++ use stream-like interface with a macro instead like BOOST_LOG does for example, (and since C++20 you could use source_location).

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • I have also replaced all instances of `l::p(...)` with `l_p(...)` to prevent macro replacement when there is already a function defined as `p(...)` – asdf Jun 02 '20 at 23:10