运算符 << - 如何检测最后一个参数

发布于 2024-09-14 10:19:41 字数 344 浏览 7 评论 0 原文

我正在用 C++ 编写一个日志类。这个类是一个单例类。我想以这样的方式添加日志:

Log::GetInstance() << "Error: " << err_code << ", in class foo";

好的,在 Log 对象内部,我想在最后一个参数出现时保存整行(本例中为“, in class foo”)。

如何检测最后一个<<争论? <<一个<< b << is_this_last << Maybe_this_is <<或不。

我不使用任何结束标签。

I'm writting a log class in c++. This class is an singleton. I want to add logs in such a way:

Log::GetInstance() << "Error: " << err_code << ", in class foo";

Ok, and inside a Log object, I want to save this whole line at the time when the last argument comes (", in class foo" in this example).

How to detect the last one << argument? << a << b << is_this_last << maybe_this_is << or_not.

I dont to use any end tags.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

贪了杯 2024-09-21 10:19:41

您可以通过不使用单例来解决这个问题。如果您创建这样的函数:

Log log()
{
    return Log();
}

您可以像之前一样添加日志:

log() << "Error: " << err_code << ", in class foo";

不同之处在于 Log 对象的析构函数在此行之后被调用。所以现在您有办法检测最后一个参数何时被处理。

You can solve this problem by not using a singleton. If you make a function like this:

Log log()
{
    return Log();
}

You can add a log almost the same way you did before:

log() << "Error: " << err_code << ", in class foo";

The difference is that the destructor of the Log object gets called after this line. So now you have a way to detect when the last argument has been processed.

何必那么矫情 2024-09-21 10:19:41

我会让您的 Log::GetInstance 返回一个代理对象而不是日志对象本身。代理对象将保存写入的数据,然后在其析构函数中,它实际上会将累积的数据写入日志。

I would have your Log::GetInstance return a proxy object instead of the log object itself. The proxy object will save the data that's written to it, and then in its destructor, it'll actually write the accumulated data to the log.

风月客 2024-09-21 10:19:41

您可以让 Log 在运算符 << 之后返回不同的对象。 。

template<typename T>
LogFindT operator<<(Log aLog, T const& data)
{
    // Put stuff in log.
    log.putStuffInLog(data);

    // now return the object to detect the end of the statement.
    return LogFindT(aLog);
}


struct LogFindT
{
    LogFindT(Log& aLog) : TheLog(aLog) {}
    Log& TheLog;
    ~LogFindT()
    {
        // Do stuff when this object is eventually destroyed
        // at the end of the expression.
    }
};

template<typename T>
LogFindT& operator<<(LogFindT& aLog, T const& data)
{
     aLog.TheLog.putStuffInLog(data);

     // Return a reference to the input so we can chain.
     // The object is thus not destroyed until the end of the stream.
     return aLog;
}

You make Log return a different object after the operator << .

template<typename T>
LogFindT operator<<(Log aLog, T const& data)
{
    // Put stuff in log.
    log.putStuffInLog(data);

    // now return the object to detect the end of the statement.
    return LogFindT(aLog);
}


struct LogFindT
{
    LogFindT(Log& aLog) : TheLog(aLog) {}
    Log& TheLog;
    ~LogFindT()
    {
        // Do stuff when this object is eventually destroyed
        // at the end of the expression.
    }
};

template<typename T>
LogFindT& operator<<(LogFindT& aLog, T const& data)
{
     aLog.TheLog.putStuffInLog(data);

     // Return a reference to the input so we can chain.
     // The object is thus not destroyed until the end of the stream.
     return aLog;
}
来世叙缘 2024-09-21 10:19:41

我认为 Jerry 和 Martin 给出了最好的建议,但为了完整起见,我首先想到的是 std::endl

如果您通过自定义 streambuf 类在 iostream 系统中实现了 Log,那么您只需添加 << endl<<在行尾刷新。既然你问了,我想你没有。

但您可以模仿 endl 的工作方式。添加操纵器处理程序

Log &operator<< ( Log &l, Log & (*manip)( Log & ) )
    { return manip( l ); } // generically call any manipulator

Log &flog( Log &l ) // define a manipulator "flush log"
    { l->flush(); return l; }

或添加专用操作符<<

struct Flog {} flog;

Log &operator<< ( Log &l, Flog )
    { l->flush(); return l; }

I think Jerry and Martin have given the best suggestion, but for the sake of completeness, the first thing I thought of was std::endl.

If you implemented Log within the iostream system by a custom streambuf class, then you can simply add << endl or << flush at the end of the line. Since you're asking, I suppose you didn't.

But you can mimic the way endl works. Either add a manipulator handler

Log &operator<< ( Log &l, Log & (*manip)( Log & ) )
    { return manip( l ); } // generically call any manipulator

Log &flog( Log &l ) // define a manipulator "flush log"
    { l->flush(); return l; }

or add a dedicated operator<<

struct Flog {} flog;

Log &operator<< ( Log &l, Flog )
    { l->flush(); return l; }
橙幽之幻 2024-09-21 10:19:41

不要对你的操作员太狡猾。当有意义时,您应该重载运算符。在这里你不应该。
这看起来很奇怪。

您应该只有一个如下所示的静态方法:

Log::Message( message_here );

它采用 std::string。然后客户就会头痛如何组装错误字符串。

Don't get too clever with your operators. You should overload operators when it makes sense to do so. Here you don't should not.
That just looks weird.

You should just have a static method that looks like this:

Log::Message( message_here );

which takes a std::string. Then clients have the head-ache of figuring out how to assemble the error string.

陪你到最终 2024-09-21 10:19:41

这是基于 @martin-york 答案的解决方案。稍作修改以在结构中使用成员运算符。

#include<sstream>
#include<iostream>

struct log_t{
    void publish(const std::string &s){
        std::cout << s << std::endl;
    }
};

struct record_t{

    struct record_appender_t
    {
        record_appender_t(record_t& record_) : record(record_) {}
        record_t& record;
        ~record_appender_t()
        {
            // Do stuff when this object is eventually destroyed
            // at the end of the expression.
            record.flush();
        }

        template<typename T>
        record_appender_t& operator<<(T const& data)
        {
            record.stream() << data;
            // Return a reference to the input so we can chain.
            // The object is thus not destroyed until the end of the stream.
            return *this;
        }
    };

    std::ostringstream message;
    log_t log;
    void flush(){
        log.publish(message.str());
    }
    std::ostringstream& stream() {
        return message;
    }
    template<typename T>
    record_appender_t operator<<(T const& data)
    {
        // Put stuff in log.
        message << data;

        // now return the object to detect the end of the statement.
        return record_appender_t(*this);
    }
};

#define LOG \
    record_t()

int main(){
    LOG << 1 << 2 << "a";
}

Here is a solution based on @martin-york's answer. Slightly modified to use member operator in the structs.

#include<sstream>
#include<iostream>

struct log_t{
    void publish(const std::string &s){
        std::cout << s << std::endl;
    }
};

struct record_t{

    struct record_appender_t
    {
        record_appender_t(record_t& record_) : record(record_) {}
        record_t& record;
        ~record_appender_t()
        {
            // Do stuff when this object is eventually destroyed
            // at the end of the expression.
            record.flush();
        }

        template<typename T>
        record_appender_t& operator<<(T const& data)
        {
            record.stream() << data;
            // Return a reference to the input so we can chain.
            // The object is thus not destroyed until the end of the stream.
            return *this;
        }
    };

    std::ostringstream message;
    log_t log;
    void flush(){
        log.publish(message.str());
    }
    std::ostringstream& stream() {
        return message;
    }
    template<typename T>
    record_appender_t operator<<(T const& data)
    {
        // Put stuff in log.
        message << data;

        // now return the object to detect the end of the statement.
        return record_appender_t(*this);
    }
};

#define LOG \
    record_t()

int main(){
    LOG << 1 << 2 << "a";
}
暗恋未遂 2024-09-21 10:19:41

没有什么好的方法可以做你想做的事。 C 和 C++ 根本就不是面向行的语言。没有像“代码行”或任何东西这样的更大的单元,也没有以任何方式组合链式调用。

在 C++ 中,表达式“a << b << c << d”完全等同于对运算符 << 的三个单独调用,如下所示:

 t1 = a;
 t2 = t1.operator<<(b);
 t3 = t2.operator<<(c);
 t4 = t3.operator<<(d);

这就是 C++ ostreams 使用 endl 作为显式结束符的原因- 线标记;否则就没有其他合适的方法可以做到这一点。

There's no good way to do what you want. C and C++ are simply not line-oriented languages. There is no larger unit like a "line of code" or anything, nor are chained calls combined in any way.

In C++ the expression "a << b << c << d" is exactly equivalent to three separate calls to operator<<, like this:

 t1 = a;
 t2 = t1.operator<<(b);
 t3 = t2.operator<<(c);
 t4 = t3.operator<<(d);

That's why C++ ostreams use endl as an explicit end-of-line marker; there's just no decent way to do it otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文