cout/cerr 以最小的努力包装 ostream

cout/cerr wrapper ostream with minimal effort

我想更好地学习 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 %d\n", i);
    i++;
    printf_log(LOG_INF, "i is %d\n", i);
    i++;
    printf_log(LOG_WRN, "i is %d\n", 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)
              ^

叮当声:

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.

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

有没有人对如何正确执行此操作有任何想法,我可以提示详细描述此操作的资源吗?

感谢任何帮助!

Log对象是常量,所以只能调用常量成员函数

你在声明后加上const关键字就说成员函数是常量

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

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

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

因为你是 "modifying the log",所以这里有常量没有多大意义。 因此,我建议完全删除 const

Log& operator<<(const T& t)

现在看起来像 std::ostream 中定义的 operator<<std::cout 的类型),这是有道理的,因为您实际上只是在为 [= 创建传递21=].

(live demo)