3

I would like to know how I can bind an allocator to a custom token of mine and use it in the token's async_result implementation?

If we take use_tuple as an example, it seems to only work with its own use_tuple.rebind(another_allocator) allocator, and not with an allocator that has been associated by the user or the object of which the async function is called of?

In my own token for my own future type, I would like to be able to say

async_read(socket, buffer, bind_allocator(some_polymorphic_alloc, my::use_future));

And in my_use_future's async_result<my::use_future_t, Signature> specialization, I want to use the bound allocator. With bind_allocator, the async_read function uses that allocator for allocations. But I don't see a path where that allocator is passed down to my my::use_future token in its async_result<>::initiate function.

sehe
  • 374,641
  • 47
  • 450
  • 633
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212

2 Answers2

4

As an introduction, bind_allocator and the completion token's internal allocator serve different purposes. I think it will become much clearer if I first address the questions in your post at face value:

  1. I would like to know how I can bind an allocator to a custom token of mine [...]

    It seems to me that bind_allocator already does what you ask:

    Live On Coliru

    auto h = [](boost::system::error_code, size_t) {};
    asio::streambuf b(1024);
    
    std::allocator<char>             a1;
    pmr::polymorphic_allocator<char> a2{pmr::get_default_resource()};
    
    async_read(s, b, h);
    async_read(s, b, asio::bind_allocator(a1, h));
    async_read(s, b, asio::bind_allocator(a2, h));
    async_read(s, b, asio::use_future);
    async_read(s, b, asio::bind_allocator(a1, asio::use_future));
    async_read(s, b, asio::bind_allocator(a2, asio::use_future));
    
  2. [...] and use it in the token's async_result implementation?

    You query it with get_associated_allocator.

  3. But I don't see a path where that allocator is passed down to my my::use_future token in its async_result<>::initiate function.

    That's because it isn't. bind_allocator(tok) returns allocator_binder< typename decay< T >::type, Allocator >. This then is the token type. Therefore any composing operations will "see" your bound associator type, but not your token internally.

How To Fix?

What you want is not merely an allocator associated with an allocator-agnostic completion token/handler. Rather you need an allocator-aware completion token.

You can create it like you would any classically allocator-aware type, e.g. for standard library types.

In fact, for example the asio::use_future token type is actually defined as asio::use_future_t<std::allocator<void> >.

As such it is both allocator-aware (the async_result specialization can respond to the chosen allocator type), and implements the associated_allocator protocol to provide Handler semantics.

I feel that the associated_XXXX protocol is closely inspired on the underused std::uses_allocator protocol. I'm using my own term "protocol" here to communicate that, in addition to an explicit trait type, there's a convention that plays together with the trait's primary template. But I digress.


Side-note

Note that allocators are less strictly adhered to than say, bound executors. The latter are essential to program correctness. For allocators, the documentation states _"Implementers can ignore the allocator, especially if the operation is not considered performance-sensitive".

sehe
  • 374,641
  • 47
  • 450
  • 633
-1

To bind an allocator to a custom token and use it in the token's async_result implementation, you need to pass the allocator to the token constructor and store it in the token. Then, when the async_result object is created, you can pass the stored allocator to the initiate function.

  • 2
    Can you quote the relevant Asio documentation or standards proposal where you can corroborate this theory? – sehe Feb 20 '23 at 13:04