Variadic functions, as implemented in C/C++, are
not type-safe at all!
Specifically,
vfprintf()
requires that the given
va_list contains one argument for each type specifier in the given format string. Furthermore, the type of each argument in the
va_list needs to match the type of the corresponding type specifier in the format string. There is, unfortunately,
no way the check this. You need to trust the caller of your
Log()
function to do it properly, every time! And, if the caller does wrong, then
undefined behavior (including crashes and possible security vulnerabilities) will be the consequence.
If you use C++ (rather than plain C), as you say, it would be
much preferable to implement a C++ (i.e. object-oriented) solution 😏
For example, something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
// ------------------
// Logging interface
// ------------------
class ILoggable {
public:
virtual void display(std::stringstream &buffer) const = 0;
};
static std::tm get_date_time(const bool local_time) {
const time_t now = time(NULL);
std::tm date;
if ((local_time ? localtime_s(&date, &now) : gmtime_s(&date, &now)) != 0) {
memset(&date, 0, sizeof(std::tm));
}
return date;
}
void log(const ILoggable *const obj) {
const std::tm date = get_date_time(false);
std::stringstream buffer;
buffer << std::put_time(&date, "[%Y-%m-%d %H:%M:%S] ");
obj->display(buffer);
std::cout << buffer.str() << std::endl;
}
// ------------------
// An example class
// ------------------
class MyClass : public ILoggable {
public:
MyClass(const int id, const char *const name) : m_id(id), m_name(name) {}
virtual void display(std::stringstream &buffer) const {
buffer << m_id << " Hello " << m_name << "!";
}
private:
const int m_id;
const std::string m_name;
};
// ------------------
// Main
// ------------------
int main()
{
MyClass instance1(1, "Alice");
MyClass instance2(2, "Bob");
log(&instance1);
log(&instance2);
}
|