2

所以这里有一个有趣的问题,我如何为 cout 制作一些类似于 wrapper 的东西?我希望能够将它添加到 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 中,我不知所措。(如何为 cout 编写一个允许表达语法的函数包装器?)因为这需要我使用 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;
    }
}

驱动程序.h

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

预期输出:

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

2 回答 2

4

您可以创建一个具有 << 运算符的结构

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“拥有”的单个实例。

于 2013-08-20T05:21:34.150 回答
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";
}

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

于 2013-08-20T09:22:54.377 回答