I am reading implementation of asio handlers using custom allocators. manual
I am trying to implement my own custom allocator using pmr. But fails in compilation with the following error
prog.cc: In instantiation of
'AsioHandler<Handler>::AsioHandler(Handler&&) [with Handler = main()::<lambda()>]':
prog.cc:77:35: required from 'AsioHandler<typename std::remove_reference<_Tp>::type> make_custom_alloc_handler(Handler&&) [with Handler = main()::<lambda()>; typename std::remove_reference<_Tp>::type =
main()::<lambda()>]'
prog.cc:84:55: required from here
prog.cc:69:24: error: cannot convert '<brace-enclosed initializer list>' to 'memory_resource*' in initialization 69 |
memory_resource * alloc_{&pool_};
| ^~~~~~
In file included from /opt/wandbox/boost-1.81.0-gcc-12.2.0/include/boost/asio.hpp:52,
from prog.cc:13: /opt/wandbox/boost-1.81.0-gcc-12.2.0/include/boost/asio/bind_executor.hpp:
In instantiation of 'boost::asio::detail::executor_binder_base<T, Executor, false>::executor_binder_base(E&&, U&&) [with E = const boost::asio::io_context::basic_executor_type<std::allocator<void>, 0>&; U = AsioHandler<main()::<lambda()> >; T = AsioHandler<main()::<lambda()> >; Executor = boost::asio::io_context::basic_executor_type<std::allocator<void>, 0>]':
/opt/wandbox/boost-1.81.0-gcc-12.2.0/include/boost/asio/bind_executor.hpp:295:46: required from 'boost::asio::executor_binder<T, Executor>::executor_binder(boost::asio::executor_arg_t, const executor_type&, U&&) [with U = AsioHandler<main()::<lambda()> >; T = AsioHandler<main()::<lambda()> >; Executor = boost::asio::io_context::basic_executor_type<std::allocator<void>, 0>; executor_type = boost::asio::io_context::basic_executor_type<std::allocator<void>, 0>]'
/opt/wandbox/boost-1.81.0-gcc-12.2.0/include/boost/asio/bind_executor.hpp:508:10: required from 'boost::asio::executor_binder<typename std::decay<_Tp2>::type, typename ExecutionContext::executor_type> boost::asio::bind_executor(ExecutionContext&, T&&, typename constraint<std::is_convertible<ExecutionContext&, execution_context&>::value>::type) [with ExecutionContext = io_context; T = AsioHandler<main()::<lambda()> >; typename ExecutionContext::executor_type = io_context::basic_executor_type<std::allocator<void>, 0>; typename std::decay<_Tp2>::type = std::decay<AsioHandler<main()::<lambda()> > ::type; typename constraint<std::is_convertible<ExecutionContext&, execution_context&>::value>::type = int]'
prog.cc:86:49: required from here
/opt/wandbox/boost-1.81.0-gcc-12.2.0/include/boost/asio/bind_executor.hpp:181:7: error: call of overloaded 'AsioHandler(AsioHandler<main()::<lambda()>)' is ambiguous
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <boost/container/pmr/global_resource.hpp>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <boost/aligned_storage.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/asio/post.hpp>
namespace bc = boost::container;
class memory_resource: public bc::pmr::memory_resource
{
public:
explicit memory_resource(bc::pmr::memory_resource* up = bc::pmr::get_default_resource())
:upstream_(up){}
void* do_allocate(size_t bytes, size_t alignment) override {
void* ret = upstream_->allocate(bytes, alignment);
return ret;
}
void do_deallocate(void* ptr, size_t bytes, size_t alignment) override {
upstream_->deallocate(ptr, bytes, alignment);
}
bool do_is_equal(const bc::pmr::memory_resource& other) const noexcept override {
return this == &other;
}
private:
bc::pmr::memory_resource* upstream_;
};
//template<typename Handler,size_t S = 2048>
constexpr std::size_t S = 2048 ;
template<typename Handler>
class AsioHandler: public Handler
{
public:
using Handler::operator();
AsioHandler(Handler && h)
:h_(std::forward<Handler>(h))
{
}
memory_resource * get_allocator() const noexcept
{
return alloc_;
}
friend void* asio_handler_allocate(std::size_t size
,AsioHandler<Handler>* this_handler)
{
return this_handler->alloc_->do_allocate(size,S);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size
,AsioHandler<Handler>* this_handler)
{
this_handler->alloc_->do_deallocate(pointer,size,S);
}
private:
Handler h_;
typename std::aligned_storage<S>::type storage_;
boost::container::pmr::monotonic_buffer_resource pool_{&storage_,S};
memory_resource * alloc_{&pool_};
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
AsioHandler<typename std::remove_reference<Handler>::type>
make_custom_alloc_handler(Handler && h)
{
return {std::forward<Handler>(h)};
}
int main()
{
boost::asio::io_context ctx;
auto func = [](){};
auto h = make_custom_alloc_handler<decltype(func)>(std::move(func));
boost::asio::post(boost::asio::bind_executor(ctx, std::move(h)));
std::thread t {[&ctx]()
{
ctx.run();
}
};
t.join();
}
How to fix this?
That's... a lot of code. It has a ton of unused headers, unnecessary threads, a duplicated Handler h_
member which is already a base class (!), the invalid forward<>
on a non-deduced rvalue reference, missing allocator_type
typedef, questionable do_is_equal
implementation, using remove_reference_t
instead of decay_t
, etc.
It seems like you hardcode the
alignment
parameter on everydo_allocate
call to be equal to theS
of the the pool... Is this intended?
Next up, your memory_resource
subclass shadows bc::pmr::memory_resource
making your code hard to grok. E.g., which did you mean to use for AsioHandler::alloc_
? I'd assume your own (let's call it my_memory_resource
for now), but the initializer isn't compatible at all. Did you mean my_memory_resource
instead of my_memory_resource*
?
Regardless of everything, pool_
being of type bc::pmc::monotonic_buffer_resource
makes AsioHandler
non-copyable by definition.
All this combined really makes me scratch my head how you even got the compiler message you posted in the first place.
I can make your code compile by fixing the non-copyability (making AsioHandler move-only), but I will not vouch for this code to be useful:
#include <boost/aligned_storage.hpp>
#include <boost/asio.hpp>
#include <boost/container/pmr/global_resource.hpp>
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <cstdlib>
#include <iostream>
// #include <boost/array.hpp>
// #include <boost/asio/post.hpp>
// #include <boost/bind/bind.hpp>
// #include <boost/enable_shared_from_this.hpp>
// #include <boost/noncopyable.hpp>
// #include <boost/shared_ptr.hpp>
namespace bc = boost::container;
class my_memory_resource : public bc::pmr::memory_resource
{
public:
explicit my_memory_resource(
bc::pmr::memory_resource* up = bc::pmr::get_default_resource())
: upstream_(up)
{
}
void* do_allocate(size_t bytes, size_t alignment) override
{
void* ret = upstream_->allocate(bytes, alignment);
return ret;
}
void do_deallocate(
void* ptr, size_t bytes, size_t alignment) override
{
upstream_->deallocate(ptr, bytes, alignment);
}
bool do_is_equal(
bc::pmr::memory_resource const& other) const noexcept override
{
return this ==
&other; // SEHE FIXME https://en.cppreference.com/w/cpp/memory/memory_resource/do_is_equal
}
private:
bc::pmr::memory_resource* upstream_;
};
// template<typename Handler,size_t S = 2048>
constexpr std::size_t S = 2048;
template <typename Handler> struct AsioHandler : Handler {
using Handler::operator();
explicit AsioHandler(Handler h) : Handler(std::move(h)) {}
my_memory_resource* get_allocator() noexcept { return alloc_.get(); }
friend void* asio_handler_allocate(std::size_t size, AsioHandler<Handler>* h) {
return h->alloc_->do_allocate(size, S);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size, AsioHandler<Handler>* h) {
h->alloc_->do_deallocate(pointer, size, S);
}
private:
// Handler h_;
typename std::aligned_storage<S>::type storage_;
std::unique_ptr<bc::pmr::monotonic_buffer_resource> pool_ =
std::make_unique<bc::pmr::monotonic_buffer_resource>(&storage_, S);
std::unique_ptr<my_memory_resource> alloc_ = std::make_unique<my_memory_resource>(pool_.get());
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler> AsioHandler<std::decay_t<Handler>> make_custom_alloc_handler(Handler&& h) {
return AsioHandler<std::decay_t<Handler>>{std::forward<Handler>(h)};
}
int main()
{
boost::asio::io_context ctx;
auto func = []() { std::cout << "SEHE WAS HERE" << std::endl; };
auto h = make_custom_alloc_handler<decltype(func)>(std::move(func));
// boost::asio::post(ctx, std::move(h));
boost::asio::post(boost::asio::bind_executor(ctx, std::move(h)));
ctx.run();
}
Prints
SEHE WAS HERE