Reputation:
I'm trying to create an asynchronous tcp server which accepts connection. I'm about to use an accept handler function object which looks like the following:
template <typename Function>
struct callback
{
Function func_;
callback()
{
}
callback(Function&& f)
: func_(std::forward<Function>(f))
{
}
template <typename ...Args>
void operator() (Args&& ...args)
{
func_(std::forward<Args>(args)...);
}
};
My server class:
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <session.hpp>
#include <handler.hpp>
class server
{
private:
typedef callback<boost::function<void(const boost::system::error_code&, session_ptr&)>> accept_handler;
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
accept_handler handler_;
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session_ptr new_session = session_ptr(new session(io_service_));
auto callback = boost::bind(&server::handle_accept,
this,
boost::asio::placeholders::error,
new_session);
handler_ = accept_handler(std::move(callback));
acceptor_.async_accept(new_session->socket(),
handler_);
}
void handle_accept(const boost::system::error_code& error, session_ptr new_session)
{
if (!error)
{
new_session->start();
new_session.reset(new session(io_service_));
acceptor_.async_accept(new_session->socket(),
handler_);
}
}
};
But when I try to compile I get following error:
error: no match for call to ‘(boost::function&)>) (const boost::system::error_code&)’ func_(std::forward(args)...);
So I must only use handler which meet AcceptHandler requirements
struct accept_handler
{
...
void operator()(
const boost::system::error_code& ec)
{
...
}
...
};
or there is a solution to use overloaded variadic template opreator() ?
Upvotes: 2
Views: 300
Reputation: 393769
UPDATED After realizing the real mistake:
The good news is: the error is quite simply fixed by changing one line:
typedef callback<boost::function<void(const boost::system::error_code&, session_ptr&)>> accept_handler;
into
typedef callback<boost::function<void(const boost::system::error_code&)>> accept_handler;
The previous definition was simply wrong for all reasons:
bind
expression: bind(&server::handle_accept, this, asio::placeholders::error, new_session)
. Note it has only 1 placeholder (asio::placeholders::error
) so it couldn't possibly work with 2 parameters#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
namespace ba = boost::asio;
using ba::ip::tcp;
struct session : std::enable_shared_from_this<session> {
session(ba::io_service& svc) : _socket(svc) {}
void start() {} // do something asynchronously
tcp::socket& socket() { return _socket; }
tcp::socket _socket;
};
using session_ptr = std::shared_ptr<session>;
template <typename Function>
struct callback
{
Function func_;
callback(Function&& f = {}) : func_(std::forward<Function>(f)) {
}
template <typename ...Args>
void operator() (Args&& ...args) {
func_(std::forward<Args>(args)...);
}
};
class server
{
private:
typedef callback<boost::function<void(const boost::system::error_code&)>> accept_handler;
ba::io_service& io_service_;
tcp::acceptor acceptor_;
accept_handler handler_;
public:
server(ba::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session_ptr new_session = session_ptr(new session(io_service_));
handler_ = accept_handler(bind(&server::handle_accept, this, ba::placeholders::error, new_session));
acceptor_.async_accept(new_session->socket(), handler_);
}
void handle_accept(const boost::system::error_code& error, session_ptr new_session)
{
if (!error)
{
new_session->start();
new_session.reset(new session(io_service_));
acceptor_.async_accept(new_session->socket(),
handler_);
}
}
};
int main() {
}
The only thing I'd note here is that quite clearly, none callback<>
, accept_handler
have any use:
class server
{
private:
ba::io_service& io_service_;
tcp::acceptor acceptor_;
public:
server(ba::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
void do_accept() {
session_ptr new_session = session_ptr(new session(io_service_));
acceptor_.async_accept(new_session->socket(), bind(&server::handle_accept, this, ba::placeholders::error, new_session));
}
void handle_accept(const boost::system::error_code& error, session_ptr new_session)
{
if (!error) {
new_session->start();
do_accept();
}
}
};
You can do without the bind, the placeholder, the handle_accept
member all at the same time:
class server
{
private:
ba::io_service& io_service_;
tcp::acceptor acceptor_;
public:
server(ba::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
void do_accept() {
session_ptr new_session = std::make_shared<session>(io_service_);
acceptor_.async_accept(new_session->socket(), [this,new_session](const boost::system::error_code& error) {
if (!error) {
new_session->start();
do_accept();
}
});
}
};
In the first reading I had assumed you ran into a classic pitfall I frequently run into with Boost Asio and polymorphic lambdas. Sorry.
Indeed, in the variadic case the concept check cannot verify the handler requirements. My approach here would be to disable the requirement checks:
#define BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS 1
The main thing lost is friendlier error messages if there is a mismatch:
Asio 1.6.0 / Boost 1.47
- Added friendlier compiler errors for when a completion handler does not meet the necessary type requirements. When C++0x is available (currently supported for g++ 4.5 or later, and MSVC 10), static_assert is also used to generate an informative error message. This checking may be disabled by defining BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS.
See http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/history.html
Upvotes: 1