It's a pity that there are a lot similar macros(both name and function) in the code below. How to simplify the similar macros?
The difference between the similar macros are the string tags, say [PROC_E]
indicats it's an error message for the module named PROC while [SERVICE_E]
indicats it's an error message for the module named SERVICE.
#include <stdio.h>
#include <type_traits>
#include <mutex>
#include <stdarg.h>
typedef enum
{
NONE,
DEBUG,
WARNING,
ERROR,
}log_level_em;
class SimpleLog
{
private:
SimpleLog() = default;
SimpleLog(const SimpleLog&) = delete;
SimpleLog& operator=(const SimpleLog&) = delete;
public:
static SimpleLog& get_instance()
{
static SimpleLog logger;
return logger;
}
void set_block_level(log_level_em level)
{
block_level = level;
}
bool block_log(log_level_em level)
{
return static_cast<std::underlying_type<log_level_em>::type>(level) <= \
static_cast<std::underlying_type<log_level_em>::type>(block_level);
}
void log_output(log_level_em level, const char* fmt, ...)
{
if(block_log(level))
{
return;
}
va_list args;
va_start(args, fmt);
{
std::unique_lock<std::mutex> lock{mtx};
vprintf(fmt, args);
}
va_end(args);
}
log_level_em block_level{NONE};
std::mutex mtx;
};
#define LOG_SET_LEVEL(lvl) \
do{ \
SimpleLog::get_instance().set_block_level(lvl); \
} while(false);
// Define the LOG_COMMON macro to accept variadic parameters
#define LOG_COMMON(log_level, fmt, ...) \
do{ \
SimpleLog::get_instance().log_output(log_level, "[%s] [%s:%d] " fmt "\n", \
__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \
} while(false);
// All the macros call LOG_COMMON
#define CAM_LOG_ERROR(fmt, ...) LOG_COMMON(ERROR, "[CAM_E] " fmt, ##__VA_ARGS__)
#define CAM_LOG_WARNING(fmt, ...) LOG_COMMON(WARNING, "[CAM_W] " fmt, ##__VA_ARGS__)
#define CAM_LOG_DEBUG(fmt, ...) LOG_COMMON(DEBUG, "[CAM_D] " fmt, ##__VA_ARGS__)
#define PROC_LOG_ERROR(fmt, ...) LOG_COMMON(ERROR, "[PROC_E] " fmt, ##__VA_ARGS__)
#define PROC_LOG_WARNING(fmt, ...) LOG_COMMON(WARNING, "[PROC_W] " fmt, ##__VA_ARGS__)
#define PROC_LOG_DEBUG(fmt, ...) LOG_COMMON(DEBUG, "[PROC_D] " fmt, ##__VA_ARGS__)
#define SERVICE_LOG_ERROR(fmt, ...) LOG_COMMON(ERROR, "[SERVICE_E] " fmt, ##__VA_ARGS__)
#define SERVICE_LOG_WARNING(fmt, ...) LOG_COMMON(WARNING, "[SERVICE_W] " fmt, ##__VA_ARGS__)
#define SERVICE_LOG_DEBUG(fmt, ...) LOG_COMMON(DEBUG, "[SERVICE_D] " fmt, ##__VA_ARGS__)
int main() {
LOG_SET_LEVEL(DEBUG);
CAM_LOG_ERROR("the fd(%d) can't be opened!", 5);
CAM_LOG_WARNING("this is a warning from %s !", "message processing thread");
PROC_LOG_ERROR("this is a warning from %s !", "message processing thread");
PROC_LOG_DEBUG("this message should not be seen");
SERVICE_LOG_WARNING("failed to run function");
return 0;
}
"[CAM_E] "
and so on. Unless you want to turn those into preprocessing tokens and paste them with##
. Either way you are not really going to reduce or simplify the macros.cam_log_*
functions?