Ok, I don't have enough code yet for a fully working program, but I'm already running into issues with "executors".
EDIT: this is Boost 1.74 -- Debian doesn't give me anything more current. Which causes problems elsewhere, but I hope it had working executors back then as well :-)
Following one of the beast examples, I'm assigning a "strand" to a number of objects (a resolver and a stream) to be slightly more future-proof in case I need to go a multithreaded-route. But that's not actually the problem here, just the reason why I used a strand.
Now, I have this object that has a number of asio "subobjects" which are all initialized with that executor. No issues there, at least on the compiler side (I don't know whether the code does the intended stuff yet... it's heavily based on a beast example, though, so it's not completely random).
So, I want to send data to that object now. The assumption here is that the entire executor stuff is kind of pointless if I just randomly manipulate stuff from the "outside", so I wanted to "dispatch" my changes to the executor to it plays nicely with the async stuff that might be going on, especially when/if threads come into play. And since all the asio objects know their executor, I figured I wouldn't need to remember it myself.
Here some random self-contained example that shows the problem I'm having.
#include <boost/asio.hpp>
/************************************************************************/
class Test
{
private:
boost::asio::ip::tcp::resolver resolver;
public:
Test(boost::asio::any_io_executor executor)
: resolver(executor)
{
}
public:
void doSomething()
{
std::function<void(void)> function;
// Doesn't compile: no "dispatch" member
resolver.get_executor().dispatch(function);
// Doesn't compile: "target" is a template, needs a type
resolver.get_executor().target()->dispatch(function);
// Compiles, but I don't like having to know that it's a strand?
// How can the asio objects use the executor without me telling them the type?
// NOTE: I don't know whether it does the right thing!
resolver.get_executor().target<boost::asio::io_context::strand>()->dispatch(function);
}
};
/************************************************************************/
void test()
{
boost::asio::io_context ioContext;
Test test(boost::asio::make_strand(ioContext));
}
It's actually all in the "doSomething()" function: how do I "dispatch" something to the same executor that some asio object uses, without having to know exactly what that executor is?
Yes, I can do the workaround and pass the "strand" object instead of any_executor, and store that with the other stuff so I have something to call directly. But since every asio object has an executor and also manages to use it properly... I should be able to do the same thing, no?
Post, defer and dispatch are free functions:
boost::asio::dispatch(resolver.get_executor(), function);
Live: http://coliru.stacked-crooked.com/a/c39d263a99fbe3fd
#include <boost/asio.hpp>
#include <iostream>
struct Test {
Test(boost::asio::any_io_executor executor) : resolver(executor) {}
void doSomething() {
boost::asio::dispatch(resolver.get_executor(), [] {std::cout << "Hello world\n";});
}
private:
boost::asio::ip::tcp::resolver resolver;
};
int main() {
boost::asio::io_context ioContext;
Test test(make_strand(ioContext));
test.doSomething();
ioContext.run();
}
Prints
Hello world