I am implementing a log function which logs along with line number of the code. The snippet of the code as follows:
#include <iostream>
using namespace std;
char m_buffer[500];
template<typename... Args>
void format_buffer(int line, const char* logData, Args... args)
{
snprintf(m_buffer, 500, logData, line, args...);
cout << m_buffer << endl;
}
template<int line = __builtin_LINE(), typename... Args>
void log_error(const char* logData, Args... args)
{
format_buffer(line, logData, args...);
}
int main()
{
float f1 = 12.35f;
log_error("Line %d: Sample error, signal value is %f",f1);
return 0;
}
Here, the output is as follows:
Line 15: Sample error, signal value is 12.350000
The line number of the template function prints here but not the log statement.
I tried using the __LINE__
macro as shown below:
#include <iostream>
using namespace std;
char m_buffer[500];
template<typename... Args>
void format_buffer(int line, const char* logData, Args... args)
{
snprintf(m_buffer, 500, logData, line, args...);
cout << m_buffer << endl;
}
template<typename... Args>
void log_error(int line, const char* logData, Args... args)
{
format_buffer(line, logData, args...);
}
int main()
{
float f1 = 12.35f;
log_error(__LINE__, "Line %d: Sample error, signal value is %f",f1);
return 0;
}
The output is:
Line 24: Sample error, signal value is 12.350000
The line number prints as the log Statement.
My concern is I don't want the user to use the __LINE__
macro in every log statement, instead I want to take the line number from the API. In fact, I don't want my function to use a format specifier for line like below.
log_error("Line %d: Sample error, signal value is %f",f1);
I want the user to use as below:
log_error("Sample error, signal value is %f",f1);
Which prints the line number automatically.
Is it possible to get the line number of the log statement without the user specifying the macro?
Note: I am using a C++14 compiler.
For a quick fix, you could use a macro, and also, skip having the user provide "Line %d: "
since that's a required part of the format string. Add that part in your log_error
function instead.
#include <cstdio>
#include <iostream>
thread_local char m_buffer[512]; // thread local to be usable from multiple threads
template <std::size_t N, class... Args>
void log_error(int line, const char (&logData)[N], Args... args) {
static_assert(N < sizeof(m_buffer) / 8, "suspiciously long format string");
auto linelen = std::snprintf(m_buffer, sizeof m_buffer, "Line %d: ", line);
if (std::snprintf(m_buffer + linelen, sizeof m_buffer - linelen,
logData, line, args...) > 0)
{
std::cout << m_buffer << std::endl;
}
}
#define LOG_ERROR(...) log_error(__LINE__, __VA_ARGS__)
int main() {
float f1 = 12.35f;
LOG_ERROR("Sample error, signal value is %f", f1);
}
Otherwise, take a look at boost::source_location
which is included in the standard library as std::source_location
since C++20.