为cout做一个包装?

时间:2013-08-20 04:13:20

标签: c++ dll wrapper cout

所以这是一个有趣的问题,我怎么能做一些像cout包装的东西? 我想能够将它添加到一个DLL中,所以我可以把它扔进我的程序。但它的基本语法应该是

    Mything::mesage << "I'm some text" << im_an_int << someclass << mything::endl;

    Mything::mesageandlog << "I'm going to print to console, and to a file!" << mything::endl;

我可以处理大部分内部逻辑,但至于我应该做什么甚至做到这一点。有点难过。

可能在我的类中创建一个名为message的静态流成员,然后在写入时通过方法运行它时会触发事件吗?

Idk,我环顾四周,找到了类似的东西,但至于把它扔进了一个dll我不知所措。 (How to write a function wrapper for cout that allows for expressive syntax?) 因为这需要我使用extern和变量,但是如何让它静态,所以我可以直接调用它而不创建变量?

有点澄清,如下: mydll.h

#include <iostream>
namespace mynamespace {

    extern struct LogMessage{};

    template <typename T>
    LogMessage& operator<< (LogMessage &s, const T &x) {
        SetStdHandle(STD_OUTPUT_HANDLE, GetStdHandle(STD_OUTPUT_HANDLE));
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE);
        std::cout << "[IF] ";
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_WHITE);
        //LogTimestamp(); --ill impliment this.
        std::cout << x << endl;
        //writeStreamToLogfile(s); --and ill handle this.
        return s;
    }
}

driverprogram.h

#include <mydll.h>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
    mynamespace::LogMessage << "Something: << std::endl;
}

预期产出:

"[IF] [00:00:00] Something

2 个答案:

答案 0 :(得分:3)

您可以创建一个结构,其中包含&lt;&lt;操作

struct OutputThing
{

  template< class T >
  OutputThing &operator<<( T val )
  {
    std::cout<<val;
    return *this;
  }
};

现在,无论何时想要记录,都必须实例化对象。

OutputThing()<<"x ="<<x;

如果您想避免重复构造和破坏对象,可以将其设为单例。

struct OutputThingSingleton
{
  static OutputThingSingleton& GetThing()
  {
    static OutputThingSingleton OutputThing;
    return OutputThing;
  }

  template< class T >
  OutputThingSingleton &operator<<( T val )
  {
    std::cout<<val;
    return *this;
  }

private:
  OutputThingSingleton()
  {};

};

所以电话现在看起来像

OutputThingSingleton::GetThing()<<"x ="<<x;

您可以使用宏缩短。

这将适用于多个dll,但是根据它的使用方式,您可以拥有单个实例的多个实例。只要你不想在你的单身人士中维持任何状态,这就可以正常工作。如果确实需要确保单个实例,可以在自己的dll中编译它。使用此dll的任何其他二进制文件将共享由dll“拥有”的单个实例。

答案 1 :(得分:2)

首先,为了给出公平的警告,我很确定这在DLL中不起作用。你想把它放在标题中(如这里所示)。

其次,它可能比您考虑的更精细。特别是,它定义了一个多输出流类,其工作方式与任何其他流类似。基本上任何operator<<的正常重载都可以正常工作。

与普通流运算符不同,输出转到多个流,每行输出(在所有流上)前面都有一个前缀(当前设置为值“[FIX] ]“,但它只是使用字符串的内容,所以你在该字符串中放置的任何内容都应该有用。更精细/完成的实现可能允许你设置类似操纵器的前缀,但是这个(当前)没有支持那个。

最后,它会执行一些可变参数模板技巧,因此您可以将输出文件指定为文件名或现有ostream对象,或其组合(例如,请参见演示文稿main)。

首先,标题:

#ifndef LOGGER_H_INC_
#define LOGGER_H_INC_

#include <iostream>
#include <streambuf>
#include <vector>
#include <fstream>


class logger: public std::streambuf {
public:
    logger(std::streambuf* s): sbuf(s) {}
    ~logger() { overflow('\n'); }
private:
    typedef std::basic_string<char_type> string;

    int_type overflow(int_type c) {

        if (traits_type::eq_int_type(traits_type::eof(), c))
            return traits_type::not_eof(c);
        switch (c) {
        case '\n':
        case '\r':  {
            prefix = "[FIX]";
            buffer += c;
            if (buffer.size() > 1)
                sbuf->sputn(prefix.c_str(), prefix.size());
            int_type rc = sbuf->sputn(buffer.c_str(), buffer.size());
            buffer.clear();
            return rc;
        }
        default:
            buffer += c;
            return c;
        }
    }

    std::string prefix;
    std::streambuf* sbuf;
    string buffer;
};

namespace multi {
    class buf : public std::streambuf {
        std::vector<std::streambuf *> buffers;
    public:
        typedef std::char_traits<char> traits_type;
        typedef traits_type::int_type  int_type;

        buf() {}

        void attach(std::streambuf *s) { buffers.push_back(s); }
        void attach(std::ofstream &s) { buffers.push_back(s.rdbuf()); }

        int_type overflow(int_type c) {
            bool eof = false;
            for (std::streambuf *buf : buffers)
                eof |= (buf->sputc(c) == traits_type::eof());
            return eof ? traits_type::eof() : c;
        }
    };

    class logstream : public std::ostream {
        std::vector<std::ofstream *> streams;
        buf outputs;
        logger log; 

        void attach(std::ostream &s) { outputs.attach(s.rdbuf()); }
        void attach(char const *name) {
            std::ofstream *s = new std::ofstream(name);
            streams.push_back(s);
            outputs.attach(s->rdbuf());
        }

        template <typename T, typename...pack>
        void attach(T &t, pack&...p) {
            attach(t);
            attach(p...);
        }

    public: 
        template <typename...pack>
        logstream(pack&...p) : log(&outputs), std::ostream(&log) { attach(p...); }

        ~logstream() {
            for (auto d : streams) {
                d->close();
                // Bug: crashes with g++ if delete is allowed to execute.
                //delete d;
            }
        }
    };
}

#endif

然后是如何使用它的演示:

#include "logger"

int main(){
    multi::logstream l(std::cout, "c:/path/log.txt");
    l << "This is a prefixed string\n";
}

显然标题相当大,但是使用它的代码似乎(至少对我来说)就像你希望的那样简单 - 创建一个对象,指定你想要输出的位置,只是一个普通的流 - 除了您可以指定多个。然后像对待任何其他流一样写入它,输出转到所有指定的输出,每行前面都有指定的前缀。