I am fairly new to boost::asio and I am trying to implement a class to wrap an std::ostream object so that all calls to the stream operator are serialized through a strand.
Ideally, this is the behavior i'm looking for:
// from thread 1
logstream << "Kiwi";
// from thread 2
logstream << "Apple";
//logstream output could be "KiwiApple" or "AppleKiwi", but
// never something like "KAppleiwi"
I have been working from other answers (here and here) explaining how to wrap an ostream
to get custom behavior, but it relies on a template argument which I can't get to play nice when I use it inside an anonymous handler passed to strand::dispatch(...)
. I should also add that I have dropped the const
modifiers for the time being as they were causing more problems (I could also use some advice on this). Here is my code:
// logstream.hpp ------------------------------------------------------------
#include <ostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
class LogStream {
public:
// construct with an io service and a unique ostream
LogStream(std::shared_ptr<boost::asio::io_service> io_service,
std::ostream &out)
: work_(*io_service), strand_(*io_service), out_(out) {};
// used to be:
// template<typename T>
// inline const LogStream & operator<<(const T& v) const {...}
template<typename T>
inline LogStream& operator<<( T& v) {
// ideally i would like to dispatch an anonymous "quickie"...
strand_.dispatch([this, v](){this->out_ << v; });
return *this;
};
private:
boost::asio::io_service::work work_;
boost::asio::strand strand_;
std::ostream &out_;
// no default constructor
LogStream() = delete;
};
// main.cpp -----------------------------------------------------------------
#include <iostream>
#include <logger/logstream.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
using namespace std;
void foo(int id, LogStream &ls) {
for (int i = 0; i < 1000; i++) {
// ls << i; // this actually seems to work
ls << "kiwi"; // compiler error - it doesn't like the array of characters.
}
}
void worker(std::shared_ptr<boost::asio::io_service> io_service) {
io_service->run();
}
int main() {
std::shared_ptr<boost::asio::io_service> io_service =
std::shared_ptr<boost::asio::io_service>(new boost::asio::io_service());
LogStream ls = LogStream(io_service, cout);
boost::thread_group workers;
for (int i = 0; i < 4; i++) {
workers.create_thread(boost::bind(&worker, io_service));
}
boost::thread_group threads;
for (int i = 0; i < 4; i++) {
threads.create_thread(boost::bind(&foo, i, ls));
}
io_service->run();
return 0;
}
I have tried a couple different variations of this basic structure (using boost::bind with a class method, for example) and the errors are different every time. As I presented it, the code now generates this compiler error:
error C2536: 'LogStream::<<::<lambda_d3e85e85f4366eae12cc7bf533b39faa>::LogStream::<<::<lambda_d3e85e85f4366eae12cc7bf533b39faa>::v' : cannot specify explicit initializer for arrays
1> d:\code projects\wfc\cpp\src\logger\logstream.hpp(28) : see declaration of 'LogStream::<<::<lambda_d3e85e85f4366eae12cc7bf533b39faa>::v'
I have looked around for info on error C2536
, but I am having a hard time relating those answers to my code. As I mentioned before, I am new to boost, and I am also not very experienced with using templates.
Answers that explain what I am doing wrong, and answers that suggest a better way to achieve the desired behavior are equally welcome. Thanks very much in advance for any help.
I am compiling with msvc120 (2013).
Aucun commentaire:
Enregistrer un commentaire