如何检测<<操作员

  • 本文关键字:操作员 何检测 c++
  • 更新时间 :
  • 英文 :


我工作的公司有一个Logger类,我们用它来安排从多个线程到窗口的打印输出。它使用printf风格的格式,我正试图创建一个包装器,使其表现得像cout。它几乎可以工作,我可以这样调用包装器类:

wlog->info() << "hello: " << 1;

并且它通过调用wlog->info()返回的helper类的析构函数在链的末尾进行刷新(这与RAII概念非常相似(。

但是,C++会发出警告,因为wlog->info()返回了一个右值,而存在警告是因为operator<<重载需要一个左值引用。有没有绕过这个警告的方法,或者检测<lt;链而不使用特殊的行尾函数/字符/事物?

在下面编辑:很抱歉花了这么长时间才在这个繁忙的周末发布圣诞准备:(我不能显示Logger类本身的代码,但它的行为与printf大致相同。调用日志->info("你好:%d",22(;将打印";信息-你好22";。

记录器_记录器.h

#include<sstream>
#include<iomanip>
#include "logger/logger.h"
namespace logger {
// helper class // can't be a nested class (i think). need to be able to return it
class LoggerHelper {
private:
Logger* log;
std::stringstream ss; // using stringstream for auto-string conversion
int level; // the type of log
int precision;
bool copy_flag;
public:
// constructor
// prefix is the name of the interface which is supplied by LoggerWrapper
// precision is floating point precision
LoggerHelper(int level_, int precision_, std::string prefix_, Logger* log_);
// copy constructor
LoggerHelper(const LoggerHelper& other);
// destrutor outputs the message to log
~LoggerHelper();
// chain together << operations
template<typename T>
friend LoggerHelper& operator<<(LoggerHelper&, const T&);
// enums for variations of log
enum LogLevels {
INFO,
DEBUG,
WARN,
ERROR,
MAX_LEVEL,
};
};
// chain together << operations
template<typename T>
LoggerHelper& operator<<(LoggerHelper& out, const T& in) {
out.ss << in;
return out;
};
// main wrapper class
class LoggerWrapper {
private:
Logger* log;
std::string prefix;
int precision; // float point precision
public:
// generic constructor
// precision defaults to 2
LoggerWrapper(std::string prefix_, Logger* log_);
// with precision
LoggerWrapper(std::string prefix_, int precision_, Logger* log_);
// not responsible for any resources
~LoggerWrapper(){}; 
// log functions
LoggerHelper info();
LoggerHelper debug();
LoggerHelper warn();
LoggerHelper error();
};
}

logger_wrapper.cpp

#include "logger_wrapper/logger_wrapper.h"
namespace logger {
// constructor
LoggerHelper::LoggerHelper(int level_, int precision_, std::string prefix_, Logger* log_) :
level(level_),
precision(precision_),
log(log_)
{
copy_flag = false;
ss << std::fixed << std::setprecision(precision);
ss << prefix_;
}
// copy constructor
LoggerHelper::LoggerHelper(const LoggerHelper& other) {
copy_flag = true;
level = other.level;
precision = other.precision;
log = other.log;
ss << std::fixed << std::setprecision(precision);
ss << other.ss.str();
}
// destrutor outputs the message to log
LoggerHelper::~LoggerHelper() {
// only run if this was a copy
if (copy_flag) {
// choose a log level
void(Logger::*func)(const char*, ...) = &Logger::info;
switch (level) {
case(LoggerHelper::DEBUG) :
func = &Logger::debug;
break;
case(LoggerHelper::WARN) :
func = &Logger::warn;
break;
case(LoggerHelper::ERROR) :
func = &Logger::error;
break;
// defaults to log->info
default:
break;
}
// output to log
(log->*func)("%s", ss.str().c_str());
}
}
// generic constructor
LoggerWrapper::LoggerWrapper(std::string prefix_, Logger* log_) :
prefix(prefix_),
precision(2),
log(log_) {}
// with precision
LoggerWrapper::LoggerWrapper(std::string prefix_, int precision_, Logger* log_) :
prefix(prefix_),
precision(precision_),
log(log_) {}
// log functions
LoggerHelper LoggerWrapper::info() {
return LoggerHelper(LoggerHelper::INFO, precision, prefix, log);
}
LoggerHelper LoggerWrapper::debug() {
return LoggerHelper(LoggerHelper::DEBUG, precision, prefix, log);
}
LoggerHelper LoggerWrapper::warn() {
return LoggerHelper(LoggerHelper::WARN, precision, prefix, log);
}
LoggerHelper LoggerWrapper::error() {
return LoggerHelper(LoggerHelper::ERROR, precision, prefix, log);
}
}

编辑2:

我尝试了bolov的答案,即将运算符重载更改为使用右值(我从未想过要尝试这个,我只是认为它必须是这样的结构(。在将所有相关函数更改为返回右值并用std::move包装后,它在没有警告的情况下编译,但抛出一个";访问违规读取";。

logger_wrapper.h 中的更改

// chain together << operations
template<typename T>
LoggerHelper&& operator<<(LoggerHelper&& out, const T& in) {
out.ss << in; // throws: Access violation reading location 0xFFFFFFFFFFFFFFFF
return std::move(out);
};

logger_wrapper.cpp 中的更改

// log functions
LoggerHelper&& LoggerWrapper::info() {
return std::move(LoggerHelper(LoggerHelper::INFO, precision, prefix, log));
}
LoggerHelper&& LoggerWrapper::debug() {
return std::move(LoggerHelper(LoggerHelper::DEBUG, precision, prefix, log));
}
LoggerHelper&& LoggerWrapper::warn() {
return std::move(LoggerHelper(LoggerHelper::WARN, precision, prefix, log));
}
LoggerHelper&& LoggerWrapper::error() {
return std::move(LoggerHelper(LoggerHelper::ERROR, precision, prefix, log));
}

我不熟悉将右值作为自变量传入。我唯一一次这样做是在编写move构造函数时。我不知道这些右值论点何时会超出范围,也不知道如何保持其"正确";生命;以确保它在行结束之前保持有效。

您应该展示您的实现,这样我们就可以看到您所尝试的内容,并在这方面为您提供帮助。

在不知道你有什么的情况下,我的解决方案是简单地使用右值引用:

info_t&& operator<<(info_t&& info, int);
{
// ...
return std::move(info);
}

返回的对象必须以某种方式将其自身标记为有效。

然后,如果被复制/移动,则将旧的标记为无效,这样它的析构函数就不会导致流被刷新。然后,当您使用operator<<时,您只需返回引用即可将该对象保留在作用域中。

这可能不是愚蠢的证明,但应该在大多数情况下工作。

#include <iostream>
class Logger
{
std::ostream*   output;  // Having a nullable stream
// allows an easy way to mark it invalid.
public:
Logger(std::ostream& s) // Created with stream.
: output(&s)
{}
~Logger()
{
// If this is valid then flush the stream.
if (output) {
(*output) << " ->Done Logging" << std::endl;
}
}
// No Copying.
Logger(Logger const&)               = delete;
Logger& operator=(Logger const&)    = delete;
// But allow it to be returned as a result from info().
Logger(Logger&& move) noexcept
: output(std::exchange(move.output, nullptr))
{}
Logger& operator=(Logger&& move) noexcept
{
output = std::exchange(move.output, nullptr);
return *this;
}
// This will allow most output operations.
// You will have to do some more work to accept
// functions (like std::endl)
template<typename T>
Logger& operator<<(T const& data)
{
(*output) << "Log: " << data;
return *this;
}
};

class Log
{
public:
Logger info()
{
return Logger(std::cerr);
}
};
int main()
{
Log log;
log.info() << "Testing: " << 1 << " 2 " << 3.0 << "Finale";
}

最新更新