一次处理所有传递的重载

时间:2016-08-21 00:08:09

标签: c++ c++11 operator-overloading cross-platform

我厌倦了在现场调试代码并在每个文件中包含<iostream>。所以我想让自己成为一个通用的,自包含的轻量级调试类,我只需要在标题中包含它,然后忘记。

我想使用

的内容
#include "debug.hpp"
debug DBG;
DBG << "foo and" << " bar";
//Or even better, just include it and do debug() << "foo and" << " bar";

所以,我写了这个:

#include <iostream>
#include <string>
#include <chrono>
#include <ctime>

class Debug
{
public:    
  Debug &operator<<(std::string arg_0)
  {
    auto tempTime = std::chrono::system_clock::to_time_t(
      std::chrono::system_clock::now() );
    auto timeString(ctime(&tempTime));
    timeString = timeString.substr(timeString.find(':') - 2, 8);

    std::cout << timeString << " >> " << arg_0 << '\n';
    return *this;
  }
};

但是,当然,这不起作用,因为正如我所知,每个重载操作符都会导致此函数(它仍被称为函数?)单独触发。创建:

hour:minute:second >> foo and
hour:minute:second >> bar

在第一个重载运算符出现后,我可以立即传递所有内容吗?也许作为一个字符串流?另外,我不会只传递字符串,而是我需要的任何内容,这是否需要我为每个可以传递的信号类型手动创建一个单独的重载函数?

P.S:跨平台解决方案是可选的,但欢迎(目前正在Linux上开发)

3 个答案:

答案 0 :(得分:6)

您可以返回另一个班级来完成这项工作,例如:

class Helper
{
public:
    ~Helper() { std::cout << "\n"; }

    template<typename T>
    friend Helper&& operator << (Helper&&h, const T& t) {
        std::cout << t;
        return std::move(h);
    }
};

class Debug
{
public:    
  template<typename T>
  friend Helper operator<<(Debug&, const T& t)
  {
    auto tempTime = std::chrono::system_clock::to_time_t(
      std::chrono::system_clock::now() );
    auto timeString{ctime(&tempTime)};
    timeString = timeString.substr(timeString.find(':') - 2, 8);

    std::cout << timeString << " >> " << t;
    return Helper{};
  }
};

答案 1 :(得分:2)

每次拨打operator<<时,您的代码都会打印时间戳\n。这就是问题所在。为避免这种情况,可以在Debug的构造函数中打印时间戳,并在析构函数中打印\n

class Debug {
public:
    Debug() {
        auto tempTime = std::chrono::system_clock::to_time_t(
                std::chrono::system_clock::now() );
        std::string timeString(ctime(&tempTime));
        timeString = timeString.substr(timeString.find(':') - 2, 8);
        std::cout << timeString;
    }
    ~Debug() {
        std::cout << "\n";
    }
    Debug &operator<<(std::string arg_0) { 
        std::cout << " >> " << arg_0;
        return *this;
    }
};

为了调试string以外的类型,您可以operator<<为模板:

template <typename T>
Debug &operator<<(T &&arg_0) {
    std::cout << " >> " << std::forward<T>(arg_0);
    return *this;
}

答案 2 :(得分:0)

我在这里看到了2个设计问题:

  1. 您尝试创建类似流的对象。这意味着,当线路结束时,它不知道,直到您向其发送EOL。没有这些信息,它不知道何时在“你的”行中添加前缀并打印它。考虑以下两种情况:

    DBG << "foo and" << " bar";

    DBG << "foo and"; ... (a lot of code) ... DBG << " bar";

    它们在Debug类中看起来完全相同,因为:

    DBG << "foo and" << " bar"; == (DBG.operator<<("foo and")).operator<<(" bar");

    这与:

    相同

    DBG.operator<<("foo and"); DBG.operator<<("bar");

    因此,您必须决定如何定义要打印的邮件的结尾(以及何时要测量时间:在邮件的开头或结尾?)。

  2. 您想什么时候刷新您的信息流?您必须将std::endlstd::flush发送至std::cout才能将其清除。发送"\n" 不会刷新std::cout(这是std::endl"\n"之间的重要区别)。如果不冲洗它,可能会在几分钟/小时后打印(它将在缓冲区中等待)。另一方面,频繁的缓冲区刷新可能是产生大量文本的应用程序中的性能杀手。 尝试定义流向您发送时的行为方式"\n"std::endlstd::flushstd::endl应转换为"\n" + {{1} })。

  3. 关于其他问题:

    1. 我会使用简单模板将std::flush的参数“转移”到operator<<()。它允许将您的类用于std :: cout可以打印的任何类型。为了简化操作,您可以在课堂外定义std::cout,例如:

      operator<<()