2016-03-20 68 views
1

我有一个struct如何更换`{lock_guard锁(obj.mut); obj.a_stringstream <<一个<< b /*...*/ << N;}`在方法调用

struct log { 
    boost::mutex mut; 
    std::stringstream a_stringstream; 
    //... 
    }; 

和在我的代码我有那些的向量并经常做

{ 
    boost::lock_guard <boost::mutex> lock(logs[AN_ENUM].mut); 
    logs[AN_ENUM].a_stringstream << "something" << a_double << a_string << etc; 
} 

每当我想记录一些东西时,似乎有详细的锁定守卫和括号。起初,我想也许我可以重载<<运算符为我的结构,但我读了它,我不认为这可以工作。有没有一种好的方法可以将这个方法压缩成方法调用?

+0

一个典型的设置是使用'logs [AN_ENUM] << a << b << c;',其中第一个'<<'返回一个包含带'<<'的析构函数完成的所有stringstream的对象工作。但是,霍华德建议使用函数调用语法的一个优点是,如果此消息低于当前级别,则可以实现日志级别,然后不会在运行时浪费时间。 –

+0

@ M.M日志级别?我无法想象析构函数会做什么。如果你愿意的话,拥有两种解决方案都是非常好的。 –

+0

“日志级别”意味着您可以为日志消息分配不同的优先级,然后在运行时(例如响应配置文件)决定显示或不显示。例如,您可能会记录对调试有用的信息,但通常不会记录该信息,除非有人遇到问题并决定将其打开。 –

回答

4

您可以创建一个可变参数模板函数(比如log_it),其采用AN_ENUM加项记录的变量数,锁定mutex一次,然后流一切a_stringstream。例如:

#include <mutex> 
#include <sstream> 
#include <vector> 

class save_stream 
{ 
    std::ostream&  os_; 
    char    fill_; 
    std::ios::fmtflags flags_; 
    std::streamsize precision_; 

public: 
    ~save_stream() 
    { 
     os_.fill(fill_); 
     os_.flags(flags_); 
     os_.precision(precision_); 
    } 

    save_stream(const save_stream&) = delete; 
    save_stream& operator=(const save_stream&) = delete; 

    explicit save_stream(std::ostream& os) 
     : os_(os) 
     , fill_(os.fill()) 
     , flags_(os.flags()) 
     , precision_(os.precision()) 
     {} 
}; 

struct log 
{ 
    std::mutex mut; 
    std::stringstream a_stringstream; 
}; 

std::vector<log> logs(10); 

enum : std::size_t {AN_ENUM}; 

void 
log_one(std::size_t) 
{ 
} 

template <class Arg0, class ...Args> 
void 
log_one(std::size_t log, Arg0 const& arg0, Args const& ...args) 
{ 
    logs[log].a_stringstream << arg0; 
    log_one(log, args...); 
} 

template <class ...Args> 
void 
log_it(std::size_t log, Args const& ...args) 
{ 
    std::lock_guard<std::mutex> lock(logs[log].mut); 
    save_stream s{logs[log].a_stringstream}; 
    logs[log].a_stringstream << "log " << log << " says : "; 
    log_one(log, args...); 
    logs[log].a_stringstream << '\n'; 
} 

这不是一个很大数量的代码。 log_it取一个整数常量,用它来索引到logs来锁定互斥锁,吐出前缀字符串,然后调用log_one带有常量日志索引和可变参数数量注销。

log_one然后简单地记录第一个参数,然后在包的其余部分递归调用log_one

它可以像这样使用:

log_it(AN_ENUM, std::fixed, std::setprecision(3), "i = ", 4.5); 
log_it(AN_ENUM,         "j = ", 4.5); 

导致logs[0].a_stringstream控股:

log 0 says : i = 4.500 
log 0 says : j = 4.5 

我敢肯定,语法可以巧妙的东西更漂亮,但得到的基本想法跨越:传递可变数量的参数,锁定在调用堆栈的顶部,然后逐个处理每个参数。

“日志级别”可能只是log_it的另一个参数。

0

为什么这个模板超载< <成员将无法工作,我想不出任何理由:

template<typename param> 
auto operator<<(param &&Param) 
{ 
    boost::lock_guard <boost::mutex> lock(mut); 

    a_stringstream << std::forward<param>(Param); 

    return *this; 
} 

我不知道你读了什么可能表明这种做法不会工作,但无论你读什么,都是错误的。我的例子可能需要一些调整,在这里和那里,取决于你的实际课程,但理论上它应该是非常可行的。

+2

这将释放每个输出值之间的锁定,可能允许另一个线程在应该是连续输出之间输出一些东西(例如,'a_double'和OP的例子中的'a_string')。 – 1201ProgramAlarm

+0

你仍然可以做到这一点,但你将不得不做更多的工作。基本上来说:让这个模板实例化一个包含锁定保护的shared_ptr和一个指向它的指针。让此模板返回shared_ptr,并将初始运算符<<参数转发给它。在shared_ptr本身声明第二个非成员运算符<<模板,其中包含我的示例中的有效内容。最终结果:该锁被获取一次,并持续整个<< s序列。如果您无法获得非成员运算符<<正在处理shared_ptr,请将shared_ptr包装到一个类中,然后在其上实现第二个运算符<<。 –

+0

...继续。如果你的应用程序日志很长,但性能可能会更糟,但仍然可行。你有没有考虑过不使用operator <<,而是一个可变的log()函数,如:log(“something”,a_double,a_string)? –

相关问题