编译出多级日志打印语句中使用的字符串- C++

jogvjijk  于 2022-12-05  发布在  其他
关注(0)|答案(2)|浏览(148)

我正在寻找一种方法来编译打印字符串出我的二进制,如果一个特定的基于宏的条件得到满足。
这里,_dLvl可以有条件地设置为等于或低于最大允许电平。

enum DEBUG_LEVELS : int
{
    DEBUG_NONE,
    DEBUG_ERRORS,
    DEBUG_WARN,
    DEBUG_INFO,
    DEBUG_VERBOSE
};

#define MAX_LEVEL  DEBUG_WARN

int _dLvl = DEBUG_ERRORS;

template <typename... Args> void E(const char * _f, Args... args){
    if (_dLvl >= DEBUG_ERRORS){
        printf(_f, args...);
    }
}
template <typename... Args> void W(const char * _f, Args... args){
    if (_dLvl >= DEBUG_WARN){
        printf(_f, args...);
    }
}
template <typename... Args> void I(const char * _f, Args... args){
    if (_dLvl >= DEBUG_INFO){
        printf(_f, args...);
    }
}
template <typename... Args> void V(const char * _f, Args... args){
    if (_dLvl >= DEBUG_VERBOSE){
        printf(_f, args...);
    }
}

int main(){
    E("This will print\n");
    W("This might be printed based on _dLvl, existence of this string is ok.\n");
    I("This wont print ever, the existence of this string is memory waste\n");
    V("Same as I\n");
}

我有一个logger类的多个示例,其中每个示例都有一个不同的MAX级别,这增加了一个挑战,请参见this question,以获得更清楚的多示例示例。
以下是我的解决方案(但这是一个丑陋且难以管理的解决方案,它要求每个示例都有一个特殊的宏,以便在源代码中以不同的方式使用):

#if (WIFI_LOG_MAX_LEVEL >= 1)
#define w_log_E(f_, ...) logger.E((f_), ##__VA_ARGS__)
#else
#define w_log_E(f_, ...)
#endif

#if (WIFI_LOG_MAX_LEVEL >= 2)
#define w_log_W(f_, ...) logger.W((f_), ##__VA_ARGS__)
#else
#define w_log_W(f_, ...)
#endif

#if (WIFI_LOG_MAX_LEVEL >= 3)
#define w_log_I(f_, ...) logger.I((f_), ##__VA_ARGS__)
#else
#define w_log_I(f_, ...)
#endif

#if (WIFI_LOG_MAX_LEVEL >= 4)
#define w_log_V(f_, ...) logger.V((f_), ##__VA_ARGS__)
#else
#define w_log_V(f_, ...)
#endif

有什么诀窍可以解决吗?

eoxn13cs

eoxn13cs1#

在条件编译块中实现日志函数,例如constexpr if将是一种现代方法:

// Sorry just a habit to order severity the other way around
enum DEBUG_LEVELS : int
{
    DEBUG_VERBOSE = 0,
    DEBUG_INFO = 1,
    DEBUG_WARN = 2,
    DEBUG_ERRORS = 3,
    DEBUG_NONE = 4
};

constexpr DEBUG_LEVELS kLevel = DEBUG_LEVELS::DEBUG_WARN;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ Log level

template <class... Args>
void warning([[maybe_unused]] const char *msg, Args&&... args)
{
    if constexpr (kLevel <= DEBUG_WARN) {
        printf(msg, std::forward<Args>(args)...);
    }
}

template <class... Args>
void info([[maybe_unused]] const char *msg, Args&&... args)
{
    if constexpr (kLevel <= DEBUG_INFO) {
        printf(msg, std::forward<Args>(args)...);
    }
}

int main()
{
    warning("Ruuuun %i", 2);
    info("Fuuuun %i", 2);
}

Demo:显示字符串"Fuuuuuun"找不到,因为info级别低于我们选择编译时使用的WARN级别。编译器检测到“空”函数体并优化掉调用

unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
`string' DB 'Ruuuun %i', 00H     ; `string'

msg$ = 8
<args_0>$ = 16
void warning<int>(char const *,int &&) PROC                  ; warning<int>, COMDAT
        mov     edx, DWORD PTR [rdx]
        jmp     printf
void warning<int>(char const *,int &&) ENDP                  ; warning<int>

msg$ = 8
<args_0>$ = 16
void info<int>(char const *,int &&) PROC               ; info<int>, COMDAT
        ret     0
void info<int>(char const *,int &&) ENDP               ; info<int>

main    PROC                                            ; COMDAT
$LN6:
        sub     rsp, 40                             ; 00000028H
        mov     edx, 2
        lea     rcx, OFFSET FLAT:`string'
        call    printf
        xor     eax, eax
        add     rsp, 40                             ; 00000028H
        ret     0
main    ENDP
2lpgd968

2lpgd9682#

这里有一个完整的答案,它是基于尼科斯Athanasiou的答案(谢谢尼科斯)。
所添加的是每个DEBUG_LEVELS枚举的模板化类,它定义了MAX_LEVEL,在编译时将在constexpr if语句中使用它,以编译出未使用的字符串。

#include <utility>
#include <cstdio>

enum DEBUG_LEVELS : int
{
    DEBUG_NONE,
    DEBUG_ERRORS,
    DEBUG_WARN,
    DEBUG_INFO,
    DEBUG_VERBOSE
};

template <int MAX_LEVEL>
class Logger {
    DEBUG_LEVELS dLvl;
public:
    void set_level(DEBUG_LEVELS level) {
        if (level > MAX_LEVEL)
            dLvl = static_cast<DEBUG_LEVELS>(MAX_LEVEL);
        else
            dLvl = level;
    }
    Logger(DEBUG_LEVELS defaultLvl) {
        if (defaultLvl > MAX_LEVEL)
            dLvl = static_cast<DEBUG_LEVELS>(MAX_LEVEL);
        else
            dLvl = defaultLvl;
    }
    template <class... Args>
    void warning([[maybe_unused]] const char *msg, Args&&... args)
    {
        if constexpr (MAX_LEVEL >= DEBUG_WARN) {
            if (dLvl >= DEBUG_WARN)
                printf(msg, std::forward<Args>(args)...);
        }
    }

    template <class... Args>
    void info([[maybe_unused]] const char *msg, Args&&... args)
    {
        if constexpr (MAX_LEVEL >= DEBUG_INFO) {
            if (dLvl >= DEBUG_INFO)
                printf(msg, std::forward<Args>(args)...);
        }
    }
};

Logger<DEBUG_WARN> logger(DEBUG_WARN);
Logger<DEBUG_INFO> logger_2(DEBUG_INFO);
int main()
{
    logger.warning("Ruuuun %i\n", 2);
    logger.info("Fuuuun %i\n", 2);
    logger_2.info("Hello\n");
    logger_2.set_level(DEBUG_NONE);
    logger_2.info("Doesn't print\n");  // Dynamically set (But the whole call and related string are optimised by the compiler..)
}

相关问题