Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Create an asynchronous post using Asio and custom allocator

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

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 every do_allocate call to be equal to the S 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:

Live On Coliru

#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
SEHE WAS HERE
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading