cout/cerr包装ostream只需最少的努力

cout/cerr wrapper ostream with minimal effort

本文关键字:努力 cerr 包装 ostream cout      更新时间:2023-10-16

我想更好地学习C++(目前我的C++仅限于它的C子集*咳嗽*…),因此我决定尝试"C++-ify"我从C到C++的一个有用的日志记录函数,(我认为)最好用代码来解释

#include <stdarg.h>
#include <stdio.h>
enum msg_type {
    LOG_DBG,
    LOG_INF,
    LOG_WRN,
    LOG_ERR
};
int printf_log(enum msg_type mt, const char *fmt_string, ...)
{
    va_list args;
    va_start(args, fmt_string);
    switch (mt) {
        case LOG_DBG:
            printf("[D] ");
            break;
        case LOG_INF:
            printf("[I] ");
            break;
        case LOG_WRN:
            printf("[W] ");
            break;
        case LOG_ERR:
            printf("[E] ");
            break;
        default:
            break;
    }
    int res = vprintf(fmt_string, args);
    va_end(args);
    return res;
}
int main()
{
    int i = 0;
    printf_log(LOG_DBG, "i is %dn", i);
    i++;
    printf_log(LOG_INF, "i is %dn", i);
    i++;
    printf_log(LOG_WRN, "i is %dn", i);
}

这应该输出:

[D] i is 0
[I] i is 1
[W] i is 2

我对C++版本的想法是有这样的东西:

#include <log.h>
int main()
{
    int i = 0;
    log::log(log::dbg)<<"i is " << i << "n";
    i++;
    log::log(log::inf)<< "i is " << i << "n";
    i++;
    log::log(log::wrn)<< "i is " << i << "n";
}

结果相同。

我特别希望避免对参数进行任何解析(可能除了log::inf或类似的),而是让它们直接传递到cout上。

但我真的不知道从哪里开始,所有关于这方面的事情要么期望更多的C++知识,要么希望你实现自己的streambuf或类似的东西。

我的想法基本上是一样的,所以我尽了最大努力,这是我在互联网上使用不同资源生成的代码:

#include <iostream>
using std::cout;
class Log {
    public:
        enum msg_type {
            dbg =1,
            inf,
            wrn,
            err
        };
        Log(enum msg_type mt)
        {
            switch (mt) {
            case dbg:
                cout << "[D] ";
                break;
            case inf:
                cout << "[I] ";
                break;
            case wrn:
                cout << "[W] ";
                break;
            case err:
                cout << "[E] ";
                break;
            default:
                break;
            }
        }
        template<typename T>
        const Log& operator<<(const T& t)
        {
            std::cout << t;
            return *this;
        }
};
int main()
{
    int i = 0;
    Log(Log::dbg)<< "i is " << i++ << "n";
    Log(Log::inf)<< "i is " << i++ << "n";
    Log(Log::inf)<< "i is " << i++ << "n";
}

显然它不起作用,但我不知道错误消息试图告诉我什么。

G++:

main.cc: In function ‘int main()’:
main.cc:46:34: error: passing ‘const Log’ as ‘this’ argument discards qualifiers [-fpermissive]
      Log(Log::dbg)<< "i is " << i++ << "n";
                                  ^
main.cc:35:14: note:   in call to ‘const Log& Log::operator<<(const T&) [with T = int]’
   const Log& operator<<(const T& t)
              ^
main.cc:46:40: error: passing ‘const Log’ as ‘this’ argument discards qualifiers [-fpermissive]
      Log(Log::dbg)<< "i is " << i++ << "n";
                                        ^
main.cc:35:14: note:   in call to ‘const Log& Log::operator<<(const T&) [with T = char [2]]’
   const Log& operator<<(const T& t)
              ^

Clang:

main.cc:46:30: error: invalid operands to binary expression ('const Log' and 'int')
        Log(Log::dbg)<< "i is " << i++ << "n";
        ~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~
main.cc:35:14: note: candidate function not viable: 'this' argument has type
      'const Log', but method is not marked const
                const Log& operator<<(const T& t)
                           ^
1 error generated.

当然,最简单的方法就是做一个宏,用例如cout << [I] <<来替换我记录的任何"伪ostream",但这远没有那么灵活。

有人对如何正确地做到这一点有什么想法吗?我可以向详细描述这一点的资源提示一下吗?

感谢您的帮助!

const Log& operator<<(const T& t)

这意味着,每次在语句中对日志对象使用<<时,都会返回一个const Log&。同一语句中的下一个链接的<<对该const Log&进行操作,该操作无法工作,因为operator<<本身未标记为const

由于您是在"修改日志",所以在这里设置constness没有多大意义因此,我建议完全删除const

Log& operator<<(const T& t)

现在它看起来像是std::ostream中定义的operator<<std::cout的类型),这是有意义的,因为您实际上只是在为std::cout创建一个传递。

(现场演示)

Log对象是常量,因此只能调用常量成员函数。

通过在声明后添加const关键字,可以说成员函数是常量。

所以你的课应该是这样的(缩写):

class Log {
    public:
        ...
        template<typename T>
        const Log& operator<<(const T& t) const
        //                                ^^^^^
        //              Note const keyword here
        {
            ...
        }
        ...
};