Discussion:
Idea about "std::pmr::memory_resource"
(too old to reply)
Mingxin Wang
2017-10-14 08:14:56 UTC
Permalink
I think `std::pmr::memory_resource` is very useful in polymorphic
programming. However, the design of `std::pmr::memory_resource` seems not
to be reasonable enough, because:
1. it could not be efficient enough in some cases where there is no demand
for polymorphism in memory allocation, and
2. it could not be efficient enough when calling the member function
`is_equal`, as the function seems not implementable without RTTI.

I think it is more reasonable to make `std::pmr::memory_resource` a
"polymorphic wrapper" that could be constructible from any concrete memory
resource, like `std::function` could be constructible from callable types,
so that the issues above could be handled.

Specifically, in order to define a "concrete memory resource", I suggest to
add the following concept:

A type MR meets the MemoryResource requirements if the following
expressions are well-formed and have the specified semantics (mr denotes a
value of type MR).

mr.allocate(bytes, alignment)
Requires: The types of `bytes` and `alignment` are both `std::size_t`.
Returns: A pointer to allocated storage with a size of at least `bytes`.
The returned storage is aligned to the specified `alignment`, if such
alignment is supported; otherwise it is aligned to `max_align`.
Throws: Appropriate exception if it is unable to allocate memory with the
requested size and alignment.

mr.deallocate(p, bytes, alignment)
Requires: The type of `p` is `void*`, the type of `bytes` and `alignment`
are both `std::size_t`; `p` shall have been returned from a prior call to
`allocate(bytes, alignment)` on a memory resource equal to `*this`, and the
storage at `p` shall not yet have been deallocated.
Effects: Dispose of allocated storage.
Throws: Nothing.

Together with corresponding type traits:

template <class T>
struct is_memory_resource;

template <class T>
inline constexpr bool is_memory_resource_v = is_memory_resource<T>::value;

I am looking forward to your comments and suggestions!

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/0a8293b4-3246-47a5-9881-bc1ca4b91772%40isocpp.org.
Nicol Bolas
2017-10-14 14:57:42 UTC
Permalink
Post by Mingxin Wang
I think `std::pmr::memory_resource` is very useful in polymorphic
programming. However, the design of `std::pmr::memory_resource` seems not
1. it could not be efficient enough in some cases where there is no demand
for polymorphism in memory allocation, and
2. it could not be efficient enough when calling the member function
`is_equal`, as the function seems not implementable without RTTI.
If you didn't need "polymorphism in memory allocation", why would you be
using polymorphic allocators? If you have some type `allocator` that can
allocate bytes, and you don't mind the fact that `vector<T, allocator>`
will be different from other `vector` types... why would you use a
`pmr::vector<T>`?

Your proposal seems like a heavy-weight version of what we already have.

And, generally speaking, people who need PMR aren't the people who turn off
RTTI in their builds.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/07ddb1f6-d3af-43df-bf96-8975c1ab4313%40isocpp.org.
Mingxin Wang
2017-10-14 17:29:38 UTC
Permalink
Thanks for your comments!
Post by Nicol Bolas
If you didn't need "polymorphism in memory allocation", why would you be
using polymorphic allocators?
This proposal is targeting at other polymorphic facilities in the standard,
such as std::function, std::any, etc., because I think `memory_resource` is
the facility with the most appropriate semantics defined in the standard
that provides an abstraction for type-independent memory allocation
algorithms. However, if I were a implementator of `std::any`, I will not
use it in the implementation due to performance considerations.
Post by Nicol Bolas
If you have some type `allocator` that can allocate bytes, and you don't
mind the fact that `vector<T, allocator>` will be different from other
`vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the polymorphism
of allocators, and this seems to have nothing to do with what I'm saying
here. Actually, I think this idea will help improve the usability of
std::any and other polymorphic facilities. Maybe we could have something
like `template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Post by Nicol Bolas
Your proposal seems like a heavy-weight version of what we already have.
And, generally speaking, people who need PMR aren't the people who turn
off RTTI in their builds.
I think RTTI is not necessary here, and a good design should avoid
unnecessary potential performance reduction in future implementation. If
the virtual function `is_equal` is removed from a memory resource (say,
·pmr::synchronized_pool_resource·), and `bool operator==(const
pmr::synchronized_pool_resource&)` is overloaded, there is no requirement
for RTTI at all, so that the runtime overhead is reduced.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2e191a96-1952-479b-b78b-22e31f8e03de%40isocpp.org.
Nicol Bolas
2017-10-14 20:39:33 UTC
Permalink
Post by Mingxin Wang
Thanks for your comments!
Post by Nicol Bolas
If you didn't need "polymorphism in memory allocation", why would you be
using polymorphic allocators?
This proposal is targeting at other polymorphic facilities in the
standard, such as std::function, std::any, etc., because I think
`memory_resource` is the facility with the most appropriate semantics
defined in the standard that provides an abstraction for type-independent
memory allocation algorithms. However, if I were a implementator of
`std::any`, I will not use it in the implementation due to performance
considerations.
OK, so you're trying to find a way to solve the "type-erased allocators
don't work in `std::function`" problem. I don't see how what you've
suggested does that.

PMR, at its core, is based on polymorphism via inheritance. That is,
dynamic polymorphism. Polymorphism based on concepts is either static
polymorphism (ie: `vector<T, someAllocatorType>`) or dynamic polymorphism
via a type-erased mechanism.

Static polymorphism isn't appropriate for `function` and `any`. And dynamic
polymorphism via type-erasure *does not work*; that's why there's a problem
to begin with.

So the only form of polymorphism that can work here is inheritance-based
polymorphism.

If you have some type `allocator` that can allocate bytes, and you don't
Post by Mingxin Wang
Post by Nicol Bolas
mind the fact that `vector<T, allocator>` will be different from other
`vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of std::any
and other polymorphic facilities. Maybe we could have something like
`template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built around
dynamic polymorphism (through type-erasure) makes no sense. Even if we were
going to do this, we'd just use the already existing Allocator concept.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/0e37b5ae-64f6-4a8a-86bd-f44b119ed40a%40isocpp.org.
Mingxin Wang
2017-10-15 04:09:40 UTC
Permalink
Post by Nicol Bolas
Post by Mingxin Wang
Thanks for your comments!
Post by Nicol Bolas
If you didn't need "polymorphism in memory allocation", why would you be
using polymorphic allocators?
This proposal is targeting at other polymorphic facilities in the
standard, such as std::function, std::any, etc., because I think
`memory_resource` is the facility with the most appropriate semantics
defined in the standard that provides an abstraction for type-independent
memory allocation algorithms. However, if I were a implementator of
`std::any`, I will not use it in the implementation due to performance
considerations.
OK, so you're trying to find a way to solve the "type-erased allocators
don't work in `std::function`" problem. I don't see how what you've
suggested does that.
PMR, at its core, is based on polymorphism via inheritance. That is,
dynamic polymorphism. Polymorphism based on concepts is either static
polymorphism (ie: `vector<T, someAllocatorType>`) or dynamic polymorphism
via a type-erased mechanism.
Static polymorphism isn't appropriate for `function` and `any`. And
dynamic polymorphism via type-erasure *does not work*; that's why there's
a problem to begin with.
I am saying that "static polymorphism *could be* appropriate for `function`
and `any`" if a concept for type-independent allocators is introduced, and
I think it should be "MemoryResource".

So the only form of polymorphism that can work here is inheritance-based
Post by Nicol Bolas
polymorphism.
If you have some type `allocator` that can allocate bytes, and you don't
Post by Mingxin Wang
Post by Nicol Bolas
mind the fact that `vector<T, allocator>` will be different from other
`vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of std::any
and other polymorphic facilities. Maybe we could have something like
`template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built
around dynamic polymorphism (through type-erasure) makes no sense. Even if
we were going to do this, we'd just use the already existing Allocator
concept.
I think "polymorphic allocator" and "allocator for polymorphic types" are
not same concepts. On the one hand, "polymorphic allocator" is the type
that could have polymorphic allocation behaviour, but could also be
type-specific (Allocator::value_type), like the class template
`std::pmr::polymorphic_allocator` does. On the other hand, "allocator for
polymorphic types" is the type that could be used to allocate memory for
any type, but may not have polymorphic allocation behaviour. I think the
semantics of "std::pmr::memory_resource" should be "allocator for
polymorphic types" instead of "polymorphic allocator", and therefore it
shall not be polymorphic.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/15f8c5ba-3a6a-4441-9620-92e157c66094%40isocpp.org.
Nicol Bolas
2017-10-15 15:12:03 UTC
Permalink
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
Thanks for your comments!
Post by Nicol Bolas
If you didn't need "polymorphism in memory allocation", why would you
be using polymorphic allocators?
This proposal is targeting at other polymorphic facilities in the
standard, such as std::function, std::any, etc., because I think
`memory_resource` is the facility with the most appropriate semantics
defined in the standard that provides an abstraction for type-independent
memory allocation algorithms. However, if I were a implementator of
`std::any`, I will not use it in the implementation due to performance
considerations.
OK, so you're trying to find a way to solve the "type-erased allocators
don't work in `std::function`" problem. I don't see how what you've
suggested does that.
PMR, at its core, is based on polymorphism via inheritance. That is,
dynamic polymorphism. Polymorphism based on concepts is either static
polymorphism (ie: `vector<T, someAllocatorType>`) or dynamic polymorphism
via a type-erased mechanism.
Static polymorphism isn't appropriate for `function` and `any`. And
dynamic polymorphism via type-erasure *does not work*; that's why
there's a problem to begin with.
I am saying that "static polymorphism *could be* appropriate for
`function` and `any`" if a concept for type-independent allocators is
introduced, and I think it should be "MemoryResource".
You can think whatever you want, but we've got evidence that it doesn't
work. `std::function` used to have a constructor that took an allocator.
That was *removed* in C++17, because nobody implemented it correctly.
Because nobody *could* implement it correctly.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r0.html>

Type-erased allocators don't work with `function`. And there's no reason to
expect that they'll work any better with `any`. Library fundamentals v2
tries to give `function` an allocator through the PMR system. That is,
replacing type erased polymorphism with inheritance polymorphism (sort of).

So the only form of polymorphism that can work here is inheritance-based
Post by Mingxin Wang
Post by Nicol Bolas
polymorphism.
If you have some type `allocator` that can allocate bytes, and you don't
Post by Mingxin Wang
Post by Nicol Bolas
mind the fact that `vector<T, allocator>` will be different from other
`vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of std::any
and other polymorphic facilities. Maybe we could have something like
`template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built
around dynamic polymorphism (through type-erasure) makes no sense. Even if
we were going to do this, we'd just use the already existing Allocator
concept.
I think "polymorphic allocator" and "allocator for polymorphic types" are
not same concepts. On the one hand, "polymorphic allocator" is the type
that could have polymorphic allocation behaviour, but could also be
type-specific (Allocator::value_type), like the class template
`std::pmr::polymorphic_allocator` does. On the other hand, "allocator for
polymorphic types" is the type that could be used to allocate memory for
any type, but may not have polymorphic allocation behaviour. I think the
semantics of "std::pmr::memory_resource" should be "allocator for
polymorphic types" instead of "polymorphic allocator", and therefore it
shall not be polymorphic.
It's that last sentence that does not follow from the rest.
`pmr::memory_resource` already is an "allocator for polymorphic types". Or
perhaps a better term for it would be "raw memory allocator", while the
Allocator concept allocates *objects*. `pmr::memory_resource` has no
interfaces for any particular `value_type`, nor is it required or even
expected to do so. So it allocates memory and that's all.

Whether `pmr::memory_resource` is itself polymorphic is essentially
orthogonal to how it allocates memory. There's no reason you can't have a
"polymorphic allocator for polymorphic types", or with better terminology a
"polymorphic raw memory allocator".

The Allocator concept defines a "static object allocator" concept.
`pmr::memory_resource` is a "polymorphic raw memory allocator" type. Sure,
we could have a "static raw memory allocator" concept, but we wouldn't
actually use it anywhere. And certainly not in the PMR system.

`pmr::memory_resource` exists so that the PMR system can have a distinction
between the allocation of raw memory and the Allocator interface to the
container. It is a polymorphic base class because the *entire purpose* of
the PMR system is to allow the allocator used by a container to not be a
part of its type. That requires substituting static polymorphism for
dynamic polymorphism.

The PMR Allocator type could in theory be implemented by type erasing the
`memory_resource`, but that wouldn't actually improve anything. Each call
into the allocation would still be an indirect call, whether it's through
virtual functions or the type-erasure mechanisms. And of course `is_equal`
would be *impossible* to implement.

You seem to have a reflexive assumption that type-erased polymorphism is
superior to inheritance-based polymorphism. The reason to use type-erasure
is if you're dealing with a case where it would be inconvenient or
impossible to modify types to use inheritance. `function` needs to be able
to take arbitrary callables, including fundamental types like function
pointers; that's the whole point of the type. `any` needs to be able to
take *anything* which is copyable, including fundamental types; that's the
whole point of the type. Inheritance polymorphism is not even an option in
those cases, since fundamental types are not inherited.

This is not the case when dealing with PMR. Raw memory allocators are not
common types, so requiring you to use a virtual interface is not exactly a
heavy burden. So, what exactly is the problem with using a virtual
interface?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/26fe4cb4-98ca-41cc-ac96-5c51851799d1%40isocpp.org.
Mingxin Wang
2017-10-15 17:05:37 UTC
Permalink
Post by Nicol Bolas
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
Thanks for your comments!
Post by Nicol Bolas
If you didn't need "polymorphism in memory allocation", why would you
be using polymorphic allocators?
This proposal is targeting at other polymorphic facilities in the
standard, such as std::function, std::any, etc., because I think
`memory_resource` is the facility with the most appropriate semantics
defined in the standard that provides an abstraction for type-independent
memory allocation algorithms. However, if I were a implementator of
`std::any`, I will not use it in the implementation due to performance
considerations.
OK, so you're trying to find a way to solve the "type-erased allocators
don't work in `std::function`" problem. I don't see how what you've
suggested does that.
PMR, at its core, is based on polymorphism via inheritance. That is,
dynamic polymorphism. Polymorphism based on concepts is either static
polymorphism (ie: `vector<T, someAllocatorType>`) or dynamic polymorphism
via a type-erased mechanism.
Static polymorphism isn't appropriate for `function` and `any`. And
dynamic polymorphism via type-erasure *does not work*; that's why
there's a problem to begin with.
I am saying that "static polymorphism *could be* appropriate for
`function` and `any`" if a concept for type-independent allocators is
introduced, and I think it should be "MemoryResource".
You can think whatever you want, but we've got evidence that it doesn't
work. `std::function` used to have a constructor that took an allocator.
That was *removed* in C++17, because nobody implemented it correctly.
Because nobody *could* implement it correctly.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r0.html>
It is true that the concept Allocator is not suitable for `std::function`,
which is because that Allocators are "type-specific" rather than
"type-erased" as they allocates memory based on the size of
"Allocator::value_type". However, the concept "MemoryResource" is
"type-erased", whose allocation behavior is based on the number of bytes,
and this is the main difference from the concept "Allocator".

I also oppose "MemoryResource" to be added to the constructor of a type
erased facility; instead, I think it would be a good idea to add it to the
class template, e.g.,

template <class T, class MR>
class function; // undefined

template <class R, class... Args, class MR>
class function<R(Args...), MR> { ... };

If there are requirements in runtime polymorphism of "MemoryResource",
users are able to use a polymorphic wrapper instead of a concrete
implementation.

Type-erased allocators don't work with `function`.
How did you conclude that?
Post by Nicol Bolas
And there's no reason to expect that they'll work any better with `any`.
Library fundamentals v2 tries to give `function` an allocator through the
PMR system. That is, replacing type erased polymorphism with inheritance
polymorphism (sort of).
So the only form of polymorphism that can work here is inheritance-based
Post by Mingxin Wang
Post by Nicol Bolas
polymorphism.
If you have some type `allocator` that can allocate bytes, and you don't
Post by Mingxin Wang
Post by Nicol Bolas
mind the fact that `vector<T, allocator>` will be different from other
`vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of std::any
and other polymorphic facilities. Maybe we could have something like
`template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built
around dynamic polymorphism (through type-erasure) makes no sense. Even if
we were going to do this, we'd just use the already existing Allocator
concept.
I think "polymorphic allocator" and "allocator for polymorphic types" are
not same concepts. On the one hand, "polymorphic allocator" is the type
that could have polymorphic allocation behaviour, but could also be
type-specific (Allocator::value_type), like the class template
`std::pmr::polymorphic_allocator` does. On the other hand, "allocator for
polymorphic types" is the type that could be used to allocate memory for
any type, but may not have polymorphic allocation behaviour. I think the
semantics of "std::pmr::memory_resource" should be "allocator for
polymorphic types" instead of "polymorphic allocator", and therefore it
shall not be polymorphic.
It's that last sentence that does not follow from the rest.
`pmr::memory_resource` already is an "allocator for polymorphic types". Or
perhaps a better term for it would be "raw memory allocator", while the
Allocator concept allocates *objects*. `pmr::memory_resource` has no
interfaces for any particular `value_type`, nor is it required or even
expected to do so. So it allocates memory and that's all.
Whether `pmr::memory_resource` is itself polymorphic is essentially
orthogonal to how it allocates memory. There's no reason you can't have a
"polymorphic allocator for polymorphic types", or with better terminology a
"polymorphic raw memory allocator".
I did not deny that "polymorphic allocator for polymorphic types" is a
reasonable demand, and that is the reason why I suggest to turn
"std::pmr::memory_resource" from a "virtual base class" into a "polymorphic
wrapper".

The Allocator concept defines a "static object allocator" concept.
Post by Nicol Bolas
`pmr::memory_resource` is a "polymorphic raw memory allocator" type. Sure,
we could have a "static raw memory allocator" concept, but we wouldn't
actually use it anywhere. And certainly not in the PMR system.
How did you conclude that "we wouldn't actually use 'static raw memory
allocator' anywhere"? I think the motivation for this was illustrated clear
enough.
Post by Nicol Bolas
`pmr::memory_resource` exists so that the PMR system can have a
distinction between the allocation of raw memory and the Allocator
interface to the container. It is a polymorphic base class because the *entire
purpose* of the PMR system is to allow the allocator used by a container
to not be a part of its type. That requires substituting static
polymorphism for dynamic polymorphism.
At the beginning, I thought it would be better to define it as a standalone
system (just as Jonathan MÃŒller did). After some research, I found that it
has exactly the same semantics as the PMR system does, and it occurs to me
that this shall be a part of the PMR system, as this will not reduce
neither usability nor performance comparing to the PMR system we have now.
Thus, I think the PMR should do more than just "allow the allocator used by
a container to not be a part of its type".
Post by Nicol Bolas
The PMR Allocator type could in theory be implemented by type erasing the
`memory_resource`, but that wouldn't actually improve anything. Each call
into the allocation would still be an indirect call, whether it's through
virtual functions or the type-erasure mechanisms.
Right. I am not trying to improve performance in runtime polymorphism, but
"compile-time polymorphism".
Post by Nicol Bolas
And of course `is_equal` would be *impossible* to implement.
How did you conclude that? As a theoretical solution, `memory_resource` may
also have member functions like`target_type` and `target` as
`std::function` does.

You seem to have a reflexive assumption that type-erased polymorphism is
Post by Nicol Bolas
superior to inheritance-based polymorphism. The reason to use type-erasure
is if you're dealing with a case where it would be inconvenient or
impossible to modify types to use inheritance.
Sometimes, it is not because "inconvenient or impossible to modify types to
use inheritance", but because some types are not always necessary to be
polymorphic at all.
Post by Nicol Bolas
`function` needs to be able to take arbitrary callables, including
fundamental types like function pointers; that's the whole point of the
type. `any` needs to be able to take *anything* which is copyable,
including fundamental types; that's the whole point of the type.
Inheritance polymorphism is not even an option in those cases, since
fundamental types are not inherited.
This is not the case when dealing with PMR. Raw memory allocators are not
common types, so requiring you to use a virtual interface is not exactly a
heavy burden. So, what exactly is the problem with using a virtual
interface?
How did you conclude that "raw memory allocators are not common types"? Why
do you think "Callable" types are "common", while "MemoryResource" types
are not?

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/fee0a310-5dc5-4421-bf49-bc8fa164fe29%40isocpp.org.
Nicol Bolas
2017-10-15 22:37:16 UTC
Permalink
I also oppose "MemoryResource" to be added to the constructor of a type
Post by Mingxin Wang
erased facility; instead, I think it would be a good idea to add it to the
class template, e.g.,
template <class T, class MR>
class function; // undefined
template <class R, class... Args, class MR>
class function<R(Args...), MR> { ... };
Why would we want this? This creates interoperation difficulty, since
`function<Prototype, Allocator1>` is a different type from
`function<Prototype, Allocator2>`. Passing them around becomes problematic.

I don't disagree with the idea of having allocation control over
type-erased objects like `function`. But making it a template parameter is
just not a good solution to this problem. `function` and `any` are
polymorphic types; invoking any of their common operations like copying or
function calling or value extraction requires the equivalent of virtual
overhead. Considering that they already have such overhead, what's wrong
with having their allocator have such overhead?

If there are requirements in runtime polymorphism of "MemoryResource",
Post by Mingxin Wang
users are able to use a polymorphic wrapper instead of a concrete
implementation.
Type-erased allocators don't work with `function`.
How did you conclude that?
Because the C++ standards committee removed it. If it were workable, then
they probably would have just made it work.

And there's no reason to expect that they'll work any better with `any`.
Post by Mingxin Wang
Post by Nicol Bolas
Library fundamentals v2 tries to give `function` an allocator through the
PMR system. That is, replacing type erased polymorphism with inheritance
polymorphism (sort of).
So the only form of polymorphism that can work here is inheritance-based
Post by Mingxin Wang
Post by Nicol Bolas
polymorphism.
If you have some type `allocator` that can allocate bytes, and you
Post by Mingxin Wang
don't mind the fact that `vector<T, allocator>` will be different from
other `vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of
std::any and other polymorphic facilities. Maybe we could have something
like `template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built
around dynamic polymorphism (through type-erasure) makes no sense. Even if
we were going to do this, we'd just use the already existing Allocator
concept.
I think "polymorphic allocator" and "allocator for polymorphic types"
are not same concepts. On the one hand, "polymorphic allocator" is the type
that could have polymorphic allocation behaviour, but could also be
type-specific (Allocator::value_type), like the class template
`std::pmr::polymorphic_allocator` does. On the other hand, "allocator for
polymorphic types" is the type that could be used to allocate memory for
any type, but may not have polymorphic allocation behaviour. I think the
semantics of "std::pmr::memory_resource" should be "allocator for
polymorphic types" instead of "polymorphic allocator", and therefore it
shall not be polymorphic.
It's that last sentence that does not follow from the rest.
`pmr::memory_resource` already is an "allocator for polymorphic types". Or
perhaps a better term for it would be "raw memory allocator", while the
Allocator concept allocates *objects*. `pmr::memory_resource` has no
interfaces for any particular `value_type`, nor is it required or even
expected to do so. So it allocates memory and that's all.
Whether `pmr::memory_resource` is itself polymorphic is essentially
orthogonal to how it allocates memory. There's no reason you can't have a
"polymorphic allocator for polymorphic types", or with better terminology a
"polymorphic raw memory allocator".
I did not deny that "polymorphic allocator for polymorphic types" is a
reasonable demand, and that is the reason why I suggest to turn
"std::pmr::memory_resource" from a "virtual base class" into a "polymorphic
wrapper".
As a base class, it's *already* a "polymorphic allocator for polymorphic
types", by your terminology. So turning it into some type-erased wrapper
accomplishes nothing.

The Allocator concept defines a "static object allocator" concept.
Post by Mingxin Wang
Post by Nicol Bolas
`pmr::memory_resource` is a "polymorphic raw memory allocator" type. Sure,
we could have a "static raw memory allocator" concept, but we wouldn't
actually use it anywhere. And certainly not in the PMR system.
How did you conclude that "we wouldn't actually use 'static raw memory
allocator' anywhere"? I think the motivation for this was illustrated clear
enough.
Where would we use it? It's not an Allocator, so it can't be used in any
containers. It's not polymorphic, so we can't use it in `function` or `any`
without suffering the problems of providing the type as a template
parameter. And using it in PMR makes no sense, since we already have a
perfectly functional PMR system that works polymorphically; it just uses a
base class rather than type-erasure.

So where exactly would we use it?

`pmr::memory_resource` exists so that the PMR system can have a distinction
Post by Mingxin Wang
Post by Nicol Bolas
between the allocation of raw memory and the Allocator interface to the
container. It is a polymorphic base class because the *entire purpose*
of the PMR system is to allow the allocator used by a container to not be a
part of its type. That requires substituting static polymorphism for
dynamic polymorphism.
At the beginning, I thought it would be better to define it as a
standalone system (just as Jonathan MÃŒller did). After some research, I
found that it has exactly the same semantics as the PMR system does, and it
occurs to me that this shall be a part of the PMR system, as this will not
reduce neither usability nor performance comparing to the PMR system we
have now. Thus, I think the PMR should do more than just "allow the
allocator used by a container to not be a part of its type".
But that's all we want from PMR.

The PMR Allocator type could in theory be implemented by type erasing the
Post by Mingxin Wang
Post by Nicol Bolas
`memory_resource`, but that wouldn't actually improve anything. Each call
into the allocation would still be an indirect call, whether it's through
virtual functions or the type-erasure mechanisms.
Right. I am not trying to improve performance in runtime polymorphism, but
"compile-time polymorphism".
And of course `is_equal` would be *impossible* to implement.
Post by Mingxin Wang
How did you conclude that? As a theoretical solution, `memory_resource`
may also have member functions like`target_type` and `target` as
`std::function` does.
`function::target_type` returns a `std::type_info`. The only way to get
such a thing is via `typeid`. Which uses RTTI. Which is *exactly* what you
castigate `is_equal` for using. Same goes for `function::target`; they both
rely on RTTI.

So again, you have gained nothing over the base class interface.

You seem to have a reflexive assumption that type-erased polymorphism is
Post by Mingxin Wang
Post by Nicol Bolas
superior to inheritance-based polymorphism. The reason to use type-erasure
is if you're dealing with a case where it would be inconvenient or
impossible to modify types to use inheritance.
Sometimes, it is not because "inconvenient or impossible to modify types
to use inheritance", but because some types are not always necessary to be
polymorphic at all.
Post by Nicol Bolas
`function` needs to be able to take arbitrary callables, including
fundamental types like function pointers; that's the whole point of the
type. `any` needs to be able to take *anything* which is copyable,
including fundamental types; that's the whole point of the type.
Inheritance polymorphism is not even an option in those cases, since
fundamental types are not inherited.
This is not the case when dealing with PMR. Raw memory allocators are not
common types, so requiring you to use a virtual interface is not exactly a
heavy burden. So, what exactly is the problem with using a virtual
interface?
How did you conclude that "raw memory allocators are not common types"?
Why do you think "Callable" types are "common", while "MemoryResource"
types are not?
Well, which does the average program have more of: different types of
memory allocators, or different types of *function signatures*? Clearly,
one is more common than the other. And you certainly have more copyable
types than you do allocators.

Allocators are special-case tools. That doesn't make them unimportant. But
having to write a wrapper or whatever to translate its interface is not a
significant burden. Compare that to having to write one for every function
pointer or copyable fundamental type.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/784ffce8-ace7-449d-a13d-a99d7336117a%40isocpp.org.
Mingxin Wang
2017-10-16 10:26:09 UTC
Permalink
Post by Mingxin Wang
I also oppose "MemoryResource" to be added to the constructor of a type
Post by Mingxin Wang
erased facility; instead, I think it would be a good idea to add it to the
class template, e.g.,
template <class T, class MR>
class function; // undefined
template <class R, class... Args, class MR>
class function<R(Args...), MR> { ... };
Why would we want this? This creates interoperation difficulty, since
`function<Prototype, Allocator1>` is a different type from
`function<Prototype, Allocator2>`. Passing them around becomes problematic.
I don't disagree with the idea of having allocation control over
type-erased objects like `function`. But making it a template parameter is
just not a good solution to this problem. `function` and `any` are
polymorphic types; invoking any of their common operations like copying or
function calling or value extraction requires the equivalent of virtual
overhead. Considering that they already have such overhead, what's wrong
with having their allocator have such overhead?
Adding a "MemoryResource" type to the template of a polymorphic facility is
positive for performance in large-scale programming. Providing there is a
class template `my_any` that has the same functionality as `std::any` does,
except that users are allowed to specify a "MemoryResource" type to
`my_any`:

template <class MR = default_memory_resource>
class my_any;

Users are able to configure the class to optimize performance. For example,
in a high-concurrency environment, users may choose pooled memory as the
memory resource to avoid contention:

using pooled_any = my_any<synchronized_memory_resource>;

If there is no particular consideration in performance, it is also allowed
to use the default memory resource, e.g. `my_any<>`.
Post by Mingxin Wang
If there are requirements in runtime polymorphism of "MemoryResource",
Post by Mingxin Wang
users are able to use a polymorphic wrapper instead of a concrete
implementation.
Type-erased allocators don't work with `function`.
How did you conclude that?
Because the C++ standards committee removed it. If it were workable, then
they probably would have just made it work.
It is true that the committee removed it from the template of the
constructor, but it was never added to the template of the class, not to
mention removing it.
Post by Mingxin Wang
And there's no reason to expect that they'll work any better with `any`.
Post by Mingxin Wang
Post by Nicol Bolas
Library fundamentals v2 tries to give `function` an allocator through the
PMR system. That is, replacing type erased polymorphism with inheritance
polymorphism (sort of).
So the only form of polymorphism that can work here is inheritance-based
Post by Mingxin Wang
Post by Nicol Bolas
polymorphism.
If you have some type `allocator` that can allocate bytes, and you
Post by Mingxin Wang
don't mind the fact that `vector<T, allocator>` will be different from
other `vector` types... why would you use a `pmr::vector<T>`?
I will only use `pmr::vector<T>` if I have requirements on the
polymorphism of allocators, and this seems to have nothing to do with what
I'm saying here.
But... *you* were the one who brought up the whole PMR system.
Post by Mingxin Wang
Actually, I think this idea will help improve the usability of
std::any and other polymorphic facilities. Maybe we could have something
like `template <class MR = ...> class any` defined in the standard in the
future, and users are free to specify any memory management strategies.
Although this will require more effort to design and review, I think the
motivation to define MemoryResouece as a concept is relatively enough.
Adding static polymorphism to a type whose *whole purpose* is built
around dynamic polymorphism (through type-erasure) makes no sense. Even if
we were going to do this, we'd just use the already existing Allocator
concept.
I think "polymorphic allocator" and "allocator for polymorphic types"
are not same concepts. On the one hand, "polymorphic allocator" is the type
that could have polymorphic allocation behaviour, but could also be
type-specific (Allocator::value_type), like the class template
`std::pmr::polymorphic_allocator` does. On the other hand, "allocator for
polymorphic types" is the type that could be used to allocate memory for
any type, but may not have polymorphic allocation behaviour. I think the
semantics of "std::pmr::memory_resource" should be "allocator for
polymorphic types" instead of "polymorphic allocator", and therefore it
shall not be polymorphic.
It's that last sentence that does not follow from the rest.
`pmr::memory_resource` already is an "allocator for polymorphic types". Or
perhaps a better term for it would be "raw memory allocator", while the
Allocator concept allocates *objects*. `pmr::memory_resource` has no
interfaces for any particular `value_type`, nor is it required or even
expected to do so. So it allocates memory and that's all.
Whether `pmr::memory_resource` is itself polymorphic is essentially
orthogonal to how it allocates memory. There's no reason you can't have a
"polymorphic allocator for polymorphic types", or with better terminology a
"polymorphic raw memory allocator".
I did not deny that "polymorphic allocator for polymorphic types" is a
reasonable demand, and that is the reason why I suggest to turn
"std::pmr::memory_resource" from a "virtual base class" into a "polymorphic
wrapper".
As a base class, it's *already* a "polymorphic allocator for polymorphic
types", by your terminology. So turning it into some type-erased wrapper
accomplishes nothing.
We could use it in allocators and polymorphic facilities, and we are able
to configure whether the memory resource should be polymorphic.

If there is no particular demand in "std::pmr::memory_resource", I have no
objection to removing it, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>"
(I am still working on that proposal).
Post by Mingxin Wang
The Allocator concept defines a "static object allocator" concept.
Post by Mingxin Wang
Post by Nicol Bolas
`pmr::memory_resource` is a "polymorphic raw memory allocator" type. Sure,
we could have a "static raw memory allocator" concept, but we wouldn't
actually use it anywhere. And certainly not in the PMR system.
How did you conclude that "we wouldn't actually use 'static raw memory
allocator' anywhere"? I think the motivation for this was illustrated clear
enough.
Where would we use it? It's not an Allocator, so it can't be used in any
containers. It's not polymorphic, so we can't use it in `function` or `any`
without suffering the problems of providing the type as a template
parameter. And using it in PMR makes no sense, since we already have a
perfectly functional PMR system that works polymorphically; it just uses a
base class rather than type-erasure.
So where exactly would we use it?
We could use it in allocators and polymorphic facilities.
Post by Mingxin Wang
`pmr::memory_resource` exists so that the PMR system can have a
Post by Mingxin Wang
Post by Nicol Bolas
distinction between the allocation of raw memory and the Allocator
interface to the container. It is a polymorphic base class because the *entire
purpose* of the PMR system is to allow the allocator used by a
container to not be a part of its type. That requires substituting static
polymorphism for dynamic polymorphism.
At the beginning, I thought it would be better to define it as a
standalone system (just as Jonathan MÃŒller did). After some research, I
found that it has exactly the same semantics as the PMR system does, and it
occurs to me that this shall be a part of the PMR system, as this will not
reduce neither usability nor performance comparing to the PMR system we
have now. Thus, I think the PMR should do more than just "allow the
allocator used by a container to not be a part of its type".
But that's all we want from PMR.
The PMR Allocator type could in theory be implemented by type erasing the
Post by Mingxin Wang
Post by Nicol Bolas
`memory_resource`, but that wouldn't actually improve anything. Each call
into the allocation would still be an indirect call, whether it's through
virtual functions or the type-erasure mechanisms.
Right. I am not trying to improve performance in runtime polymorphism,
but "compile-time polymorphism".
And of course `is_equal` would be *impossible* to implement.
Post by Mingxin Wang
How did you conclude that? As a theoretical solution, `memory_resource`
may also have member functions like`target_type` and `target` as
`std::function` does.
`function::target_type` returns a `std::type_info`. The only way to get
such a thing is via `typeid`. Which uses RTTI. Which is *exactly* what
you castigate `is_equal` for using. Same goes for `function::target`; they
both rely on RTTI.
So again, you have gained nothing over the base class interface.
You seem to have a reflexive assumption that type-erased polymorphism is
Post by Mingxin Wang
Post by Nicol Bolas
superior to inheritance-based polymorphism. The reason to use type-erasure
is if you're dealing with a case where it would be inconvenient or
impossible to modify types to use inheritance.
Sometimes, it is not because "inconvenient or impossible to modify types
to use inheritance", but because some types are not always necessary to be
polymorphic at all.
Post by Nicol Bolas
`function` needs to be able to take arbitrary callables, including
fundamental types like function pointers; that's the whole point of the
type. `any` needs to be able to take *anything* which is copyable,
including fundamental types; that's the whole point of the type.
Inheritance polymorphism is not even an option in those cases, since
fundamental types are not inherited.
This is not the case when dealing with PMR. Raw memory allocators are
not common types, so requiring you to use a virtual interface is not
exactly a heavy burden. So, what exactly is the problem with using a
virtual interface?
How did you conclude that "raw memory allocators are not common types"?
Why do you think "Callable" types are "common", while "MemoryResource"
types are not?
Well, which does the average program have more of: different types of
memory allocators, or different types of *function signatures*? Clearly,
one is more common than the other. And you certainly have more copyable
types than you do allocators.
It just provides some significative options. If there is little particular
considerations, using default configurations is the same as befure.
Post by Mingxin Wang
Allocators are special-case tools. That doesn't make them unimportant. But
having to write a wrapper or whatever to translate its interface is not a
significant burden. Compare that to having to write one for every function
pointer or copyable fundamental type.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d029c18a-12ee-4a7d-828c-ab1bd07b58c6%40isocpp.org.
Aleksey Loginov
2017-10-16 10:55:41 UTC
Permalink
пПМеЎельМОк, 16 Пктября 2017 г., 13:26:09 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Mingxin Wang
I also oppose "MemoryResource" to be added to the constructor of a type
Post by Mingxin Wang
erased facility; instead, I think it would be a good idea to add it to the
class template, e.g.,
template <class T, class MR>
class function; // undefined
template <class R, class... Args, class MR>
class function<R(Args...), MR> { ... };
Why would we want this? This creates interoperation difficulty, since
`function<Prototype, Allocator1>` is a different type from
`function<Prototype, Allocator2>`. Passing them around becomes problematic.
I don't disagree with the idea of having allocation control over
type-erased objects like `function`. But making it a template parameter is
just not a good solution to this problem. `function` and `any` are
polymorphic types; invoking any of their common operations like copying or
function calling or value extraction requires the equivalent of virtual
overhead. Considering that they already have such overhead, what's wrong
with having their allocator have such overhead?
Adding a "MemoryResource" type to the template of a polymorphic facility
is positive for performance in large-scale programming. Providing there is
a class template `my_any` that has the same functionality as `std::any`
does, except that users are allowed to specify a "MemoryResource" type to
template <class MR = default_memory_resource>
class my_any;
Users are able to configure the class to optimize performance. For
example, in a high-concurrency environment, users may choose pooled memory
using pooled_any = my_any<synchronized_memory_resource>;
If there is no particular consideration in performance, it is also allowed
to use the default memory resource, e.g. `my_any<>`.
Let's look at a more applied example:

template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size> m_data;
};

template<typename MR>
struct my_any;


my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;

x1 = 1;
x2 = x1;

assert(x2 == 1);


How can you do this without RTTI?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6d0ad98b-87d5-43dd-ac54-2b7d3e03e31b%40isocpp.org.
Mingxin Wang
2017-10-16 11:55:41 UTC
Permalink
Post by Aleksey Loginov
template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size> m_data;
};
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
What is the semantics of `x2 == 1`? There is no definition about
`operation==(...)` in the class `std::any`, and this expression is
ill-formed if `x2` is a value of `std::any`. Do you mean that
`any::operator==(const any&)` shall be added to the standard? If so, I
suggest that you should illustrate your motivation and provide reasonable
semantics.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/72ef3209-6749-4246-9c5a-1549557ceb5a%40isocpp.org.
Aleksey Loginov
2017-10-16 12:17:52 UTC
Permalink
пПМеЎельМОк, 16 Пктября 2017 г., 14:55:41 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size> m_data
; };
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
What is the semantics of `x2 == 1`? There is no definition about
`operation==(...)` in the class `std::any`, and this expression is
ill-formed if `x2` is a value of `std::any`. Do you mean that
`any::operator==(const any&)` shall be added to the standard? If so, I
suggest that you should illustrate your motivation and provide reasonable
semantics.
No, this is just test of result assignment (can be rewritten as any_cast<x2>
)
Main question is: how you can implement operator= without having base
*memory_resource *for MR.

PS: in standard it will be more useful to get inner_resource in
std::memory_resource for chaining (similar to *scoped_allocator_adaptor*)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/58cdd9a2-3f73-4991-8fad-ef753634a4f0%40isocpp.org.
Mingxin Wang
2017-10-16 12:08:45 UTC
Permalink
Post by Aleksey Loginov
template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size> m_data;
};
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
And I don't think `x2` should be copy-assignable from `x1`, if

- `static_resource<32>` is not copy-constructible from
`default_memory_resource`, or
- the two memory resources could not be reflexive (being reflexive
means, you could allocate some memory from one, and deallocate the memory
from another).

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bd58ac93-5d20-48e6-93cc-b07a727ce892%40isocpp.org.
Aleksey Loginov
2017-10-16 12:26:57 UTC
Permalink
пПМеЎельМОк, 16 Пктября 2017 г., 15:08:45 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size> m_data
; };
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
And I don't think `x2` should be copy-assignable from `x1`, if
- `static_resource<32>` is not copy-constructible from
`default_memory_resource`, or
- the two memory resources could not be reflexive (being reflexive
means, you could allocate some memory from one, and deallocate the memory
from another).
So x1 = 1 is valid, but x2 = x1 is not valid? Why?
x2 doesn't deallocates from default_memory_resource. It work only with
static_resource.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4af80cf7-244c-4137-9133-1a36a209e4ca%40isocpp.org.
Mingxin Wang
2017-10-16 15:11:02 UTC
Permalink
Post by Aleksey Loginov
пПМеЎельМОк, 16 Пктября 2017 г., 15:08:45 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
template<size_t Size>
struct static_resource : memory_resource { aligned_storage_t<Size>
m_data; };
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
And I don't think `x2` should be copy-assignable from `x1`, if
- `static_resource<32>` is not copy-constructible from
`default_memory_resource`, or
- the two memory resources could not be reflexive (being reflexive
means, you could allocate some memory from one, and deallocate the memory
from another).
So x1 = 1 is valid, but x2 = x1 is not valid? Why?
x2 doesn't deallocates from default_memory_resource. It work only with
static_resource.
I am sorry, `x2 = x1` *should* be a valid expression. I misjudged the code
and thought it were an assignment between memory resources.

The class template `my_any` may declare any `my_any` types as friend have
overloads of `operator=` for them:

template <class _MR>
friend class my_any;

template <class _MR>
my_any& operator=(const my_any<_MR>&);

template <class _MR>
my_any& operator=(my_any<_MR>&&);

In order to prove that the class template `my_any` is implementable, I have
roughly implemented one that supports various of constructors, `operator=`,
`type()` and `get()`. You may use `type()` and `get()` to implement
those non-member function templates like `any_cast`.

struct manager_t {
public:
template <class T>
constexpr explicit manager_t(std::in_place_type_t<T>)
: type(typeid(T)),
do_copy(do_copy_impl<T>),
do_move(do_move_impl<T>),
do_destroy(do_destroy_impl<T>),
size(sizeof(T)) {}

constexpr explicit manager_t(std::nullptr_t)
: type(typeid(void)),
do_copy(do_copy_null),
do_move(do_move_null),
do_destroy(do_destroy_null),
size(0) {}

constexpr bool soo_activated() const {
return size <= sizeof(void*);
}

template <class MR>
void copy(void** from, void** to, MR& mr) const {
if (soo_activated()) {
do_copy(from, to);
} else {
*to = mr.allocate(size, ALIGNMENT);
do_copy(*from, *to);
}
}

void* get(void** data) const {
return soo_activated() ? data : *data;
}

template <class MR>
void move(void** from, void** to, MR& mr) const {
if (soo_activated()) {
do_move(from, to);
} else {
*to = mr.allocate(size, ALIGNMENT);
do_move(*from, *to);
}
}

template <class MR>
void destroy(void** data, MR& mr) const {
if (soo_activated()) {
do_destroy(data);
} else {
do_destroy(*data);
mr.deallocate(*data, size, ALIGNMENT);
}
}

const std::type_info& type;

static inline constexpr std::size_t ALIGNMENT = alignof(std::max_align_t);

private:
void(*do_copy)(void*, void*);
void(*do_move)(void*, void*);
void(*do_destroy)(void*);
std::size_t size;

template <class T>
static inline void do_copy_impl(void* from, void* to) {
new (reinterpret_cast<T*>(to)) T(*reinterpret_cast<const T*>(from));
}

template <class T>
static inline void do_move_impl(void* from, void* to) {
new (reinterpret_cast<T*>(to))
T(std::move(*reinterpret_cast<T*>(from)));
}

template <class T>
static inline void do_destroy_impl(void* data) {
reinterpret_cast<T*>(data)->~T();
}

static inline void do_copy_null(void*, void*) {}
static inline void do_move_null(void*, void*) {}
static inline void do_destroy_null(void*) {}
};

template <class T>
inline constexpr manager_t MANAGER{std::in_place_type<T>};

inline constexpr manager_t NULL_MANAGER{nullptr};

template <class MR>
struct memory_resource_user {
MR mr_;
};

template <class MR>
class my_any;

template <class T>
struct is_my_any : std::false_type {};

template <class MR>
struct is_my_any<my_any<MR>> : std::true_type {};

template <class T>
inline constexpr bool is_my_any_v = is_my_any<T>::value;

template <class MR>
class my_any : public memory_resource_user<MR> {
template <class>
friend class my_any;

public:
template <class T>
my_any(T&& data) requires !is_my_any_v<std::decay_t<T>>
{ init(std::in_place_type<std::decay_t<T>>, std::forward<T>(data)); }

template <class T, class... Args>
explicit my_any(std::in_place_type_t<T>, Args&&... args)
{ init(std::in_place_type<T>, std::forward<Args>(args)...); }

template<class T, class U, class... Args>
explicit my_any(std::in_place_type_t<T>, std::initializer_list<U> il,
Args&&... args)
{ init(std::in_place_type<T>, il, std::forward<Args>(args)...); }

constexpr my_any() noexcept { init(); }

my_any(const my_any& rhs) { copy_init(rhs); }
my_any(my_any&& rhs) noexcept { move_init(rhs); }

template <class _MR>
my_any(const my_any<_MR>& rhs) { copy_init(rhs); }

template <class _MR>
my_any(my_any<_MR>&& rhs) noexcept { move_init(rhs); }

~my_any() { deinit(); }

template <class T>
my_any& operator=(T&& data) requires !is_my_any_v<std::decay_t<T>> {
deinit();
init(std::in_place_type<std::decay_t<T>>, std::forward<T>(data));
return *this;
}

template <class _MR>
my_any& operator=(const my_any<_MR>& rhs) {
deinit();
copy_init(rhs);
return *this;
}

template <class _MR>
my_any& operator=(my_any<_MR>&& rhs) {
deinit();
move_init(rhs);
return *this;
}

void* get() const noexcept {
return manager_->get(&data_);
}

bool has_value() const noexcept {
return manager_ != &NULL_MANAGER;
}

const std::type_info& type() const noexcept {
return manager_->type;
}

private:
void init() { manager_ = &NULL_MANAGER; }

template <class T, class... Args>
void init(std::in_place_type_t<T>, Args&&... args) requires
(MANAGER<T>.soo_activated()) {
manager_ = &MANAGER<T>;
new (reinterpret_cast<T*>(&data_)) T(std::forward<Args>(args)...);
}

template <class T, class... Args>
void init(std::in_place_type_t<T>, Args&&... args) requires
(!MANAGER<T>.soo_activated()) {
manager_ = &MANAGER<T>;
data_ = this->mr_.allocate(sizeof(T), manager_t::ALIGNMENT);
new (reinterpret_cast<T*>(data_)) T(std::forward<Args>(args)...);
}

template <class _MR>
void copy_init(const my_any<_MR>& rhs) {
manager_ = rhs.manager_;
manager_->copy(&rhs.data_, &data_, this->mr_);
}

template <class _MR>
void move_init(my_any<_MR>& rhs) {
manager_ = rhs.manager_;
manager_->move(&rhs.data_, &data_, this->mr_);
}

void deinit() {
manager_->destroy(&data_, this->mr_);
}

const manager_t* manager_;
mutable void* data_;
};

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7f22751b-91d1-4de4-81d2-421046ceb963%40isocpp.org.
Aleksey Loginov
2017-10-16 17:39:33 UTC
Permalink
пПМеЎельМОк, 16 Пктября 2017 г., 18:11:02 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
пПМеЎельМОк, 16 Пктября 2017 г., 15:08:45 UTC+3 пПльзПватель Mingxin Wang
Post by Aleksey Loginov
template<size_t Size>
Post by Aleksey Loginov
struct static_resource : memory_resource { aligned_storage_t<Size>
m_data; };
template<typename MR>
struct my_any;
my_any<default_memory_resource> x1;
my_any<static_resource<32>> x2;
x1 = 1;
x2 = x1;
assert(x2 == 1);
How can you do this without RTTI?
The class template `my_any` may declare any `my_any` types as friend
template <class _MR>
friend class my_any;
template <class _MR>
my_any& operator=(const my_any<_MR>&);
template <class _MR>
my_any& operator=(my_any<_MR>&&);
In order to prove that the class template `my_any` is implementable, I
have roughly implemented one that supports various of constructors,
`operator=`, `type()` and `get()`. You may use `type()` and `get()` to
implement those non-member function templates like `any_cast`.
struct manager_t {
template <class T>
constexpr explicit manager_t(std::in_place_type_t<T>)
: type(typeid(T)),
do_copy(do_copy_impl<T>),
do_move(do_move_impl<T>),
do_destroy(do_destroy_impl<T>),
size(sizeof(T)) {}
template <class MR>
void copy(void** from, void** to, MR& mr) const {
if (soo_activated()) {
do_copy(from, to);
} else {
*to = mr.allocate(size, ALIGNMENT);
do_copy(*from, *to);
}
}
const std::type_info& type;
void(*do_copy)(void*, void*);
void(*do_move)(void*, void*);
void(*do_destroy)(void*);
std::size_t size;
template <class T>
static inline void do_copy_impl(void* from, void* to) {
new (reinterpret_cast<T*>(to)) T(*reinterpret_cast<const T*>(from));
}
};
template <class T>
inline constexpr manager_t MANAGER{std::in_place_type<T>};
template <class MR>
struct memory_resource_user {
MR mr_;
};
template <class MR>
class my_any : public memory_resource_user<MR> {
template <class>
friend class my_any;
my_any(const my_any& rhs) { copy_init(rhs); }
template <class _MR>
my_any(const my_any<_MR>& rhs) { copy_init(rhs); }
~my_any() { deinit(); }
void init() { manager_ = &NULL_MANAGER; }
template <class _MR>
void copy_init(const my_any<_MR>& rhs) {
manager_ = rhs.manager_;
manager_->copy(&rhs.data_, &data_, this->mr_);
}
void deinit() {
manager_->destroy(&data_, this->mr_);
}
const manager_t* manager_;
mutable void* data_;
};
Mingxin Wang
In other words, by hand-made RTTI, and only for fixed allocation
starategies (for example, data_ can not be a shared_ptr<T> or cow_ptr<T>)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/68690e5c-f466-4fbe-ab41-80be58257259%40isocpp.org.
Mingxin Wang
2017-10-17 01:29:15 UTC
Permalink
Post by Aleksey Loginov
In other words, by hand-made RTTI, and only for fixed allocation
starategies (for example, data_ can not be a shared_ptr<T> or cow_ptr<T>)
There is NO RTTI in the implementation of "operator=". This operation is
done via indirect function calls, and it is as efficient as most
implementations of `std::any` from various vendors.

`data_` is actually implementation detail, I don't know about your
motivation that it should be shared_ptr<T> or cow_ptr<T>.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/664105b3-ea68-4565-8017-a4893f4b38bf%40isocpp.org.
Aleksey Loginov
2017-10-17 06:22:07 UTC
Permalink
втПрМОк, 17 Пктября 2017 г., 4:29:15 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
In other words, by hand-made RTTI, and only for fixed allocation
starategies (for example, data_ can not be a shared_ptr<T> or cow_ptr<T>)
There is NO RTTI in the implementation of "operator=". This operation is
done via indirect function calls, and it is as efficient as most
implementations of `std::any` from various vendors.
`data_` is actually implementation detail, I don't know about your
motivation that it should be shared_ptr<T> or cow_ptr<T>.
Mingxin Wang
You are just simulate RTTI via function pointers and meta-type pointer (const
manager_t*).

Motivation is simple: do not copy big data, when it's not needed. Passing
policy in constructor like in_place_type_t can do the job.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/551eb493-f49d-412d-98d2-9a5d96f68104%40isocpp.org.
Mingxin Wang
2017-10-17 07:16:09 UTC
Permalink
Post by Aleksey Loginov
втПрМОк, 17 Пктября 2017 г., 4:29:15 UTC+3 пПльзПватель Mingxin Wang
Post by Mingxin Wang
Post by Aleksey Loginov
In other words, by hand-made RTTI, and only for fixed allocation
starategies (for example, data_ can not be a shared_ptr<T> or cow_ptr<T>)
There is NO RTTI in the implementation of "operator=". This operation is
done via indirect function calls, and it is as efficient as most
implementations of `std::any` from various vendors.
`data_` is actually implementation detail, I don't know about your
motivation that it should be shared_ptr<T> or cow_ptr<T>.
Mingxin Wang
You are just simulate RTTI via function pointers and meta-type pointer (const
manager_t*).
Motivation is simple: do not copy big data, when it's not needed. Passing
policy in constructor like in_place_type_t can do the job.
You are right about the demand in different lifetime management strategies.
Unfortunately, `std::any` and `std::function` do no support customization
in this aspect. For this reason, I designed things like `SharedWrapper`,
`DeepWrapper` and `DefferedWrapper` in my polymorphism support system ("The
Proxies - A Language Feature Decoupling Implementations from Requirements
of Polymorphism", proposal is still in process).

However, I do not agree with the idea that we should "pass different
policies to the constructors" to choose proper lifetime management
strategy, because such strategy should be a property that associated with
each wrapper, and therefore I tend to define polymorphic wrappers
associating with different lifetime management strategies as different
types.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e96fb60b-e69a-4ae6-a2c4-f3d33e88166b%40isocpp.org.
Nicol Bolas
2017-10-16 20:44:16 UTC
Permalink
Post by Mingxin Wang
Post by Mingxin Wang
I also oppose "MemoryResource" to be added to the constructor of a type
Post by Mingxin Wang
erased facility; instead, I think it would be a good idea to add it to the
class template, e.g.,
template <class T, class MR>
class function; // undefined
template <class R, class... Args, class MR>
class function<R(Args...), MR> { ... };
Why would we want this? This creates interoperation difficulty, since
`function<Prototype, Allocator1>` is a different type from
`function<Prototype, Allocator2>`. Passing them around becomes problematic.
I don't disagree with the idea of having allocation control over
type-erased objects like `function`. But making it a template parameter is
just not a good solution to this problem. `function` and `any` are
polymorphic types; invoking any of their common operations like copying or
function calling or value extraction requires the equivalent of virtual
overhead. Considering that they already have such overhead, what's wrong
with having their allocator have such overhead?
Adding a "MemoryResource" type to the template of a polymorphic facility
is positive for performance in large-scale programming.
Where is the "large-scale programming" that needs "large-scale" quantities
of `any` and/or `function` or similar type-erased types? Generally
speaking, such tools are not used in high-performance code, not so much
because of allocation behavior, but because they require type-erasure and
therefore are more expensive than static polymorphism.
Post by Mingxin Wang
Providing there is a class template `my_any` that has the
same functionality as `std::any` does, except that users are allowed to
template <class MR = default_memory_resource>
class my_any;
Users are able to configure the class to optimize performance. For
example, in a high-concurrency environment, users may choose pooled memory
using pooled_any = my_any<synchronized_memory_resource>;
If there is no particular consideration in performance, it is also allowed
to use the default memory resource, e.g. `my_any<>`.
Post by Mingxin Wang
If there are requirements in runtime polymorphism of "MemoryResource",
Post by Mingxin Wang
users are able to use a polymorphic wrapper instead of a concrete
implementation.
Type-erased allocators don't work with `function`.
How did you conclude that?
Because the C++ standards committee removed it. If it were workable, then
they probably would have just made it work.
It is true that the committee removed it from the template of the
constructor, but it was never added to the template of the class, not to
mention removing it.
Post by Mingxin Wang
I did not deny that "polymorphic allocator for polymorphic types" is a
Post by Mingxin Wang
reasonable demand, and that is the reason why I suggest to turn
"std::pmr::memory_resource" from a "virtual base class" into a "polymorphic
wrapper".
As a base class, it's *already* a "polymorphic allocator for polymorphic
types", by your terminology. So turning it into some type-erased wrapper
accomplishes nothing.
We could use it in allocators and polymorphic facilities, and we are able
to configure whether the memory resource should be polymorphic.
Which we can already do. `pmr::memory_resource` is a polymorphic type, so *by
definition* it can be used in "polymorphic facilities". And you can very
much build allocators out of them; that's what a
`pmr::polymorphic_allocator<T>` *is*.

If you wanted, you could make a `pmr::static_allocator<T, MemoryResource>`
template that takes its `pmr::memory_resource`-derived class by type. As
such, it can de-virtualize any calls to the allocation/deallocation
functions. Thus, it would be essentially zero overhead.

So what *exactly* can you not do currently that you want to do?

If there is no particular demand in "std::pmr::memory_resource", I have no
Post by Mingxin Wang
objection to removing it, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>"
(I am still working on that proposal).
But we do have a "particular demand" for it; that's why it was
standardized. If there was no demand, we wouldn't have standardized it.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/60f71623-0a56-478e-8dcc-e76685fc8706%40isocpp.org.
Mingxin Wang
2017-10-17 03:35:55 UTC
Permalink
Post by Nicol Bolas
Post by Mingxin Wang
Adding a "MemoryResource" type to the template of a polymorphic facility
is positive for performance in large-scale programming.
Where is the "large-scale programming" that needs "large-scale" quantities
of `any` and/or `function` or similar type-erased types? Generally
speaking, such tools are not used in high-performance code, not so much
because of allocation behavior, but because they require type-erasure and
therefore are more expensive than static polymorphism.
I disagree with that. I think polymorphic requirements are more likely to
appear in large-scale program, because independent modules are difficult to
be implemented with templates, and therefore they tend to accept
polymorphic input. For example, the "threadpool" is a common facility in
large-scale programming, and it seems that no one could implement such
facility without polymorphism (more specifically, function pointers).

Actually, allocation is much more expensive than polymorphism in various
implementations for `std::any`. In order to prove that, I have run a couple
of performance tests for the "template constructor" (that is, construct
from an arbitrary type) and "copy constructor" of `std::any` with empty
trivial types whose size range from 1 to 64.

Here is the environment information:

CPU: Intel® core(TM) i7-4700HQ @ 2.40GHz
Memory: 12GB
OS: CentOS 7.2
Compiler: gcc 7.1.0 x64 posix
Compiler Flags: -Wall -fexceptions -march=corei7-avx –O3 -m64 -fconcepts
-std=c++17

And the experimental results are as follows:

<Loading Image...>

<Loading Image...>

It is apparent that the execution time has increased significantly when the
size of the test class changes from 8 to 9. It is because that the size for
"Small Object Optimization" is 8 (defined by the implementation of
`std::any`). When the size becomes larger, there are memory allocations,
and allocations consume much more time than polymorphism.

We could use it in allocators and polymorphic facilities, and we are able
Post by Nicol Bolas
Post by Mingxin Wang
to configure whether the memory resource should be polymorphic.
Which we can already do. `pmr::memory_resource` is a polymorphic type, so *by
definition* it can be used in "polymorphic facilities". And you can very
much build allocators out of them; that's what a
`pmr::polymorphic_allocator<T>` *is*.
If you wanted, you could make a `pmr::static_allocator<T, MemoryResource>`
template that takes its `pmr::memory_resource`-derived class by type. As
such, it can de-virtualize any calls to the allocation/deallocation
functions. Thus, it would be essentially zero overhead.
However, this compiler optimization does not hold under all circumstances,
especially when there are classes derived from a
"`pmr::memory_resource`-derived class", unless it is explicitly declared
"final".

Besides, a "`pmr::memory_resource`-derived class" is certainly not empty,
even if it is stateless; so that `pmr::static_allocator<T,
`pmr::memory_resource`-derived>` could not be empty either. Even if the
compiler could deduce that a "`pmr::memory_resource`-derived class" is
accessible without any dispatcher, there is no chance to optimize the size.
Post by Nicol Bolas
So what *exactly* can you not do currently that you want to do?
If there is no particular demand in "std::pmr::memory_resource", I have no
Post by Mingxin Wang
objection to removing it, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>"
(I am still working on that proposal).
But we do have a "particular demand" for it; that's why it was
standardized. If there was no demand, we wouldn't have standardized it.
OK, that's no big deal, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>".
I will add it into that proposal as a use case.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d41189b5-cf47-4ddc-bf3c-0274394015f2%40isocpp.org.
Nicol Bolas
2017-10-17 19:32:37 UTC
Permalink
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
Adding a "MemoryResource" type to the template of a polymorphic facility
is positive for performance in large-scale programming.
Where is the "large-scale programming" that needs "large-scale"
quantities of `any` and/or `function` or similar type-erased types?
Generally speaking, such tools are not used in high-performance code, not
so much because of allocation behavior, but because they require
type-erasure and therefore are more expensive than static polymorphism.
I disagree with that. I think polymorphic requirements are more likely to
appear in large-scale program, because independent modules are difficult to
be implemented with templates, and therefore they tend to accept
polymorphic input. For example, the "threadpool" is a common facility in
large-scale programming, and it seems that no one could implement such
facility without polymorphism (more specifically, function pointers).
OK, there seems to be a misunderstanding as to what "large-scale" meant. I
took that to mean "used a lot in performance-critical code." You seemed to
take that to mean "large program."

Allow me to rephrase what I said, taking this into account: who cares?

Unless you are in performance-critical code, performance doesn't matter.
However "large-scale" or "small-scale" the code is, unless the allocation
is happening in the part of the code that is critical for performance, you
shouldn't care where the memory comes from.

So unless adding tasks to a threadpool is a significant part of your code's
performance (and it's hard to see how that could be the case), there's no
reason to have a *static* facility in `function` for overriding allocation
behavior. A dynamic polymorphic facility would make sense, but again, we
can use `pmr::memory_resource` for that as is.

Actually, allocation is much more expensive than polymorphism in various
Post by Mingxin Wang
implementations for `std::any`.
... nobody suggested otherwise. The only question is whether that code is
performance-critical, and whether the performance difference between static
polymorphism and dynamic polymorphism will matter.

We could use it in allocators and polymorphic facilities, and we are able
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
to configure whether the memory resource should be polymorphic.
Which we can already do. `pmr::memory_resource` is a polymorphic type, so *by
definition* it can be used in "polymorphic facilities". And you can very
much build allocators out of them; that's what a
`pmr::polymorphic_allocator<T>` *is*.
If you wanted, you could make a `pmr::static_allocator<T,
MemoryResource>` template that takes its `pmr::memory_resource`-derived
class by type. As such, it can de-virtualize any calls to the
allocation/deallocation functions. Thus, it would be essentially zero
overhead.
However, this compiler optimization does not hold under all circumstances,
especially when there are classes derived from a
"`pmr::memory_resource`-derived class", unless it is explicitly declared
"final".
I was thinking that it would simply call `MR::allocate` to allocate the
bytes, where `MR` is the memory resource type passed to the template. That
ought to bypass the virtual mechanism. But then I looked at
`pmr::memory_resource` and saw that the actual virtual functions are
private. It can still be done; make `pmr::memory_resource` declare
`static_allocator<T, MR>` a friend of itself, so that it can directly call
`do_allocate` non-virtually.

Besides, a "`pmr::memory_resource`-derived class" is certainly not empty,
Post by Mingxin Wang
even if it is stateless; so that `pmr::static_allocator<T,
`pmr::memory_resource`-derived>` could not be empty either. Even if the
compiler could deduce that a "`pmr::memory_resource`-derived class" is
accessible without any dispatcher, there is no chance to optimize the size.
Is `static_allocator` going to derive from it? No; it has to hold a pointer
to it, since one ought to be able to change memory resources. So the
instance of `memory_resource` is going to have to take up storage
*somewhere*.

So let's look at this objectively. Thus far, the objective benefits from
enforcing the use of type-erased polymorphism for memory resources rather
than using a virtual base class is that... you save a pointer's worth of
bytes. But *only* when you're using static polymorphism; if you're actually
using dynamic polymorphism via type-erasure, that cost comes back.

Is saving those few bytes really important enough to bother?

Also... when exactly would a memory resource not have state? If we're
talking about optimizing memory allocations, you would generally be using
some kind of pool. And since you're optimizing memory allocations, you
don't want to make this some global state (as that would require mutex
locks or something similar on allocate/deallocate calls). So the
memory_resource class would either contain the pool or reference the pool.
Either way, it has state.

If you're doing one of those "arena" allocators where you just advance an
pointer, you might be able to get away with making it global by making the
pointer atomic. But having multiple arena allocators is also something you
might want, and there's no reason to create multiple types just to have
that (which bloats code size due to template usage). So such resources
probably access their arenas via either containing the arena or referencing
it.

So what *exactly* can you not do currently that you want to do?
Post by Mingxin Wang
Post by Nicol Bolas
If there is no particular demand in "std::pmr::memory_resource", I have
Post by Mingxin Wang
no objection to removing it, because it is easily implementable with "the
proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>"
(I am still working on that proposal).
But we do have a "particular demand" for it; that's why it was
standardized. If there was no demand, we wouldn't have standardized it.
OK, that's no big deal, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>".
I will add it into that proposal as a use case.
Um... `pmr::memory_resource` *already exists* in the standard. It's not
going to be removed just because you think you've found something better.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/342fbace-1540-4333-9fd4-f2ee8012e852%40isocpp.org.
Mingxin Wang
2017-10-19 09:18:20 UTC
Permalink
Post by Nicol Bolas
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
Adding a "MemoryResource" type to the template of a polymorphic
facility is positive for performance in large-scale programming.
Where is the "large-scale programming" that needs "large-scale"
quantities of `any` and/or `function` or similar type-erased types?
Generally speaking, such tools are not used in high-performance code, not
so much because of allocation behavior, but because they require
type-erasure and therefore are more expensive than static polymorphism.
I disagree with that. I think polymorphic requirements are more likely to
appear in large-scale program, because independent modules are difficult to
be implemented with templates, and therefore they tend to accept
polymorphic input. For example, the "threadpool" is a common facility in
large-scale programming, and it seems that no one could implement such
facility without polymorphism (more specifically, function pointers).
OK, there seems to be a misunderstanding as to what "large-scale" meant. I
took that to mean "used a lot in performance-critical code." You seemed to
take that to mean "large program."
Allow me to rephrase what I said, taking this into account: who cares?
Unless you are in performance-critical code, performance doesn't matter.
However "large-scale" or "small-scale" the code is, unless the allocation
is happening in the part of the code that is critical for performance, you
shouldn't care where the memory comes from.
It is true that the performance of memory allocation is not likely to be a
bottleneck in some cases. However, the performance issue was introduced to
prove that the PMR system is somewhat problematic, and it also reflects to
issues about usability and extendibility.

For usability, as a user, I dislike using the memory resources with
pointers (so does references), because we are responsible for managing the
lifetime of the concrete memory resources, e.g. via new/delete. If
`std::memory_resource` is defined as a polymorphic wrapper, it is no longer
an issue, because the wrapper could manage the lifetime of the concrete
entity in a proper way. Moreover, with the support from my upcoming “proxy
system”, we could also have more lifetime management strategies from
“aggregated relationship” to “weak association relationship”.

For extendibility, as an implementer of a 3rd party library extending the
PMR system, I do dislike overriding a virtual function like
`std::pmr::memory_resource::do_is_equal`, which forces us to use RTTI just
to compare whether two memory resources are reflexive. If there is no such
“virtual base”, it becomes easy for us to produce such logic with some
proper overloads of `operator==`.

So unless adding tasks to a threadpool is a significant part of your code's
Post by Nicol Bolas
performance (and it's hard to see how that could be the case), there's no
reason to have a *static* facility in `function` for overriding
allocation behavior. A dynamic polymorphic facility would make sense, but
again, we can use `pmr::memory_resource` for that as is.
Yes, “we can use `pmr::memory_resource`”, but there is a better choice.
Post by Nicol Bolas
Actually, allocation is much more expensive than polymorphism in various
Post by Mingxin Wang
implementations for `std::any`.
... nobody suggested otherwise. The only question is whether that code is
performance-critical, and whether the performance difference between static
polymorphism and dynamic polymorphism will matter.
We could use it in allocators and polymorphic facilities, and we are able
Post by Mingxin Wang
Post by Nicol Bolas
Post by Mingxin Wang
to configure whether the memory resource should be polymorphic.
Which we can already do. `pmr::memory_resource` is a polymorphic type,
so *by definition* it can be used in "polymorphic facilities". And you
can very much build allocators out of them; that's what a
`pmr::polymorphic_allocator<T>` *is*.
If you wanted, you could make a `pmr::static_allocator<T,
MemoryResource>` template that takes its `pmr::memory_resource`-derived
class by type. As such, it can de-virtualize any calls to the
allocation/deallocation functions. Thus, it would be essentially zero
overhead.
However, this compiler optimization does not hold under all
circumstances, especially when there are classes derived from a
"`pmr::memory_resource`-derived class", unless it is explicitly declared
"final".
I was thinking that it would simply call `MR::allocate` to allocate the
bytes, where `MR` is the memory resource type passed to the template. That
ought to bypass the virtual mechanism. But then I looked at
`pmr::memory_resource` and saw that the actual virtual functions are
private. It can still be done; make `pmr::memory_resource` declare
`static_allocator<T, MR>` a friend of itself, so that it can directly call
`do_allocate` non-virtually.
It is doable, but less elegant. If we are not using a function
polymorphically, why bother defining it as a virtual one?
Post by Nicol Bolas
Besides, a "`pmr::memory_resource`-derived class" is certainly not empty,
Post by Mingxin Wang
even if it is stateless; so that `pmr::static_allocator<T,
`pmr::memory_resource`-derived>` could not be empty either. Even if the
compiler could deduce that a "`pmr::memory_resource`-derived class" is
accessible without any dispatcher, there is no chance to optimize the size.
Is `static_allocator` going to derive from it? No; it has to hold a
pointer to it, since one ought to be able to change memory resources. So
the instance of `memory_resource` is going to have to take up storage
*somewhere*.
And we are also responsible for managing the lifetime of the concrete
memory resources.
Post by Nicol Bolas
So let's look at this objectively. Thus far, the objective benefits from
enforcing the use of type-erased polymorphism for memory resources rather
than using a virtual base class is that... you save a pointer's worth of
bytes. But *only* when you're using static polymorphism; if you're
actually using dynamic polymorphism via type-erasure, that cost comes back.
Is saving those few bytes really important enough to bother?
`Saving those few bytes` is not important in most cases. What important is
that the PMR system is wasting those few bytes once you want to use it not
polymorphically.

Actually, the benefits in adding the concept “MemoryResource” include not
only potentially higher performance, but also clearer semantics, more
usability and more extendibility.
Post by Nicol Bolas
Also... when exactly would a memory resource not have state? If we're
talking about optimizing memory allocations, you would generally be using
some kind of pool. And since you're optimizing memory allocations, you
don't want to make this some global state (as that would require mutex
locks or something similar on allocate/deallocate calls). So the
memory_resource class would either contain the pool or reference the pool.
Either way, it has state.
`std::pmr::new_delete_resource` is actually stateless.
Post by Nicol Bolas
If you're doing one of those "arena" allocators where you just advance an
pointer, you might be able to get away with making it global by making the
pointer atomic. But having multiple arena allocators is also something you
might want, and there's no reason to create multiple types just to have
that (which bloats code size due to template usage). So such resources
probably access their arenas via either containing the arena or referencing
it.
I never denied the necessity in polymorphic allocators, and there is no
conflict between “the demand in polymorphic memory resources” and “the
demand in non-polymorphic ones”.
Post by Nicol Bolas
So what *exactly* can you not do currently that you want to do?
Post by Mingxin Wang
Post by Nicol Bolas
If there is no particular demand in "std::pmr::memory_resource", I have
Post by Mingxin Wang
no objection to removing it, because it is easily implementable with "the
proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>"
(I am still working on that proposal).
But we do have a "particular demand" for it; that's why it was
standardized. If there was no demand, we wouldn't have standardized it.
OK, that's no big deal, because it is easily implementable with "the proxy
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/lfAr1ef22YM>".
I will add it into that proposal as a use case.
Um... `pmr::memory_resource` *already exists* in the standard. It's not
going to be removed just because you think you've found something better.
I think it is somewhat subjective to draw such a conclusion before a panel
discussion by the committee.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6ed3bbca-1cee-4b69-a527-29109d4d5d5f%40isocpp.org.
T. C.
2017-10-14 19:54:40 UTC
Permalink
For simple memory resources, including the three in the standard, is_equal
implementations can simply do an address comparison. There's no need for
dynamic_cast or RTTI.

That the standard currently depicts dynamic_casts for them is a defect.
See https://cplusplus.github.io/LWG/issue3000
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/62cd9e1e-03b8-4125-a032-72624a9f5254%40isocpp.org.
Mingxin Wang
2017-10-15 03:40:29 UTC
Permalink
Thanks for your comments!
Post by T. C.
For simple memory resources, including the three in the standard, is_equal
implementations can simply do an address comparison. There's no need for
dynamic_cast or RTTI.
However, this conclusion only holds in "simple designs", and it is apparent
that `std::pmr::memory_resource` is desined to have good extendability for
more complex implementations in the future, as what is illustrated in the
latest draft: "The most-derived type of other might not match the type of
this. For a derived class D, a typical implementation of this function will
immediately return false if dynamic_cast<const D*>(&other) == nullptr".

Meanwhile, the `operator==` defined in `std::pmr::memory_resource` seems
not so well-designed:

bool operator==(const memory_resource& a, const memory_resource& b)
Post by T. C.
noexcept;
Returns: &a == &b || a.is_equal(b).
For "simple designs", as you said, `&a == &b` and `a.is_equal(b)` seems to
be equivalent, so that redundant operations are introduced.
Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ac2e1965-b697-4be3-bd33-c9431dd2619d%40isocpp.org.
Jonathan Müller
2017-10-15 06:48:48 UTC
Permalink
On Oct 14, 2017 10:14, "Mingxin Wang" <***@163.com> wrote:

Specifically, in order to define a "concrete memory resource", I suggest to
add the following concept:

A type MR meets the MemoryResource requirements if the following
expressions are well-formed and have the specified semantics (mr denotes a
value of type MR).

mr.allocate(bytes, alignment)
Requires: The types of `bytes` and `alignment` are both `std::size_t`.
Returns: A pointer to allocated storage with a size of at least `bytes`.
The returned storage is aligned to the specified `alignment`, if such
alignment is supported; otherwise it is aligned to `max_align`.
Throws: Appropriate exception if it is unable to allocate memory with the
requested size and alignment.

mr.deallocate(p, bytes, alignment)
Requires: The type of `p` is `void*`, the type of `bytes` and `alignment`
are both `std::size_t`; `p` shall have been returned from a prior call to
`allocate(bytes, alignment)` on a memory resource equal to `*this`, and the
storage at `p` shall not yet have been deallocated.
Effects: Dispose of allocated storage.
Throws: Nothing.


<plug>
It is non-standard but my memory library provides an allocator concept
similar to those you've described. You can find it here:
github.com/foonathan/memory.
</plug>

I agree that making the PMRs polymorphic by default wasn't an ideal choice.
They should have been template based and provide a type erasure wrapper.

However, I've heard that the virtual overhead doesn't really matter much.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAEddoJZzSZ5RROJ6Uu%3D2%2BR90mXi%2BhnHPDCxobrHM2daM6AxPVw%40mail.gmail.com.
Michał Dominiak
2017-10-15 07:49:15 UTC
Permalink
It's memory allocation, folks. Unless you're doing a forever growing
allocator that just keeps a pointer to the next block and doesn't allow
freeing the memory, the virtual dispatch cost is going to get drowned in
the cost of actually, you know, allocating the memory.

People need to stop microoptimizing every cycle in silly places like this,
and focus on the things that actually make their systems slow.

On Sun, Oct 15, 2017 at 8:48 AM Jonathan MÃŒller <
Post by Mingxin Wang
Specifically, in order to define a "concrete memory resource", I suggest
A type MR meets the MemoryResource requirements if the following
expressions are well-formed and have the specified semantics (mr denotes a
value of type MR).
mr.allocate(bytes, alignment)
Requires: The types of `bytes` and `alignment` are both `std::size_t`.
Returns: A pointer to allocated storage with a size of at least `bytes`.
The returned storage is aligned to the specified `alignment`, if such
alignment is supported; otherwise it is aligned to `max_align`.
Throws: Appropriate exception if it is unable to allocate memory with
the requested size and alignment.
mr.deallocate(p, bytes, alignment)
Requires: The type of `p` is `void*`, the type of `bytes` and
`alignment` are both `std::size_t`; `p` shall have been returned from a
prior call to `allocate(bytes, alignment)` on a memory resource equal to
`*this`, and the storage at `p` shall not yet have been deallocated.
Effects: Dispose of allocated storage.
Throws: Nothing.
<plug>
It is non-standard but my memory library provides an allocator concept
github.com/foonathan/memory.
</plug>
I agree that making the PMRs polymorphic by default wasn't an ideal
choice. They should have been template based and provide a type erasure
wrapper.
However, I've heard that the virtual overhead doesn't really matter much.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAEddoJZzSZ5RROJ6Uu%3D2%2BR90mXi%2BhnHPDCxobrHM2daM6AxPVw%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAEddoJZzSZ5RROJ6Uu%3D2%2BR90mXi%2BhnHPDCxobrHM2daM6AxPVw%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAPCFJdQUrdiHDcZtziJbm6H62osF0o2V2QFU0h1t0ZjyjY7qcw%40mail.gmail.com.
Jonathan Müller
2017-10-15 10:34:25 UTC
Permalink
Post by Michał Dominiak
It's memory allocation, folks. Unless you're doing a forever growing
allocator that just keeps a pointer to the next block and doesn't allow
freeing the memory, the virtual dispatch cost is going to get drowned in
the cost of actually, you know, allocating the memory.
But this is a completely reasonable allocator you might want to use.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/3fc0d268-657a-c2a6-79f6-b9174eeddb41%40gmail.com.
Mingxin Wang
2017-10-15 12:35:27 UTC
Permalink
Post by Jonathan Müller
<plug>
It is non-standard but my memory library provides an allocator concept
github.com/foonathan/memory
<http://www.google.com/url?q=http%3A%2F%2Fgithub.com%2Ffoonathan%2Fmemory&sa=D&sntz=1&usg=AFQjCNGmTNxiSSIhJkiOi3R3FNCN2L_2Mw>
.
</plug>
I agree that making the PMRs polymorphic by default wasn't an ideal
choice. They should have been template based and provide a type erasure
wrapper.
However, I've heard that the virtual overhead doesn't really matter much.
Although the "virtual overhead" is acceptible in many cases, it is an
evidence that "PMRs" still has room to improve.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/60cbae8d-2a6d-4d1b-9fdf-5c4c913ee0a0%40isocpp.org.
Nicol Bolas
2017-10-15 15:14:38 UTC
Permalink
Post by Mingxin Wang
Post by Jonathan Müller
<plug>
It is non-standard but my memory library provides an allocator concept
github.com/foonathan/memory
<http://www.google.com/url?q=http%3A%2F%2Fgithub.com%2Ffoonathan%2Fmemory&sa=D&sntz=1&usg=AFQjCNGmTNxiSSIhJkiOi3R3FNCN2L_2Mw>
.
</plug>
I agree that making the PMRs polymorphic by default wasn't an ideal
choice. They should have been template based and provide a type erasure
wrapper.
However, I've heard that the virtual overhead doesn't really matter much.
Although the "virtual overhead" is acceptible in many cases, it is an
evidence that "PMRs" still has room to improve.
Replacing "virtual overhead" with "type-erased access overhead" is not an
improvement.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/97632d14-327a-4af6-8e55-35e753f9557c%40isocpp.org.
Jonathan Müller
2017-10-15 15:48:52 UTC
Permalink
Post by Mingxin Wang
Although the "virtual overhead" is acceptible in many cases, it is
an evidence that "PMRs" still has room to improve.
Replacing "virtual overhead" with "type-erased access overhead" is not
an improvement.
It would be an improvement if you could decide between a type erased
allocator and a template allocator.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/18b21c5c-ab0f-51ed-1b00-d0cda5c7c046%40gmail.com.
Nicol Bolas
2017-10-15 16:21:41 UTC
Permalink
Post by Jonathan Müller
Post by Mingxin Wang
Although the "virtual overhead" is acceptible in many cases, it is
an evidence that "PMRs" still has room to improve.
Replacing "virtual overhead" with "type-erased access overhead" is not
an improvement.
It would be an improvement if you could decide between a type erased
allocator and a template allocator.
I don't know what you mean by "template allocator". If you're talking about
like `vector<T, Allocator>`, well, we already have that right now. Users of
containers can decide between a virtual allocator and a "template
allocator". Why does "type-erased" need to be an option, specifically?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5accfbfb-91b3-489e-9e2d-b82149b5ce8e%40isocpp.org.
Jonathan Müller
2017-10-15 16:27:39 UTC
Permalink
Post by Mingxin Wang
    Although the "virtual overhead" is acceptible in many cases,
it is
    an evidence that "PMRs" still has room to improve.
Replacing "virtual overhead" with "type-erased access overhead"
is not
an improvement.
It would be an improvement if you could decide between a type erased
allocator and a template allocator.
I don't know what you mean by "template allocator". If you're talking
about like `vector<T, Allocator>`, well, we already have that right now.
Users of containers can decide between a virtual allocator and a
"template allocator". Why does "type-erased" need to be an option,
specifically?
Yes, I meant `vector<T, Allocator>` and yes, we have both options
already, so yes, I don't think OP's idea is necessary.

However, for a hypothetical redesign of the standard library I think
this should be considered.
An allocator interface similar to the simple PMRs as opposed to the
complexity mess that is Allocator, but instead of virtual functions and
class hierarchy it is "concept-based" like Allocator and provides the
possibility of using type erasure.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4aa86771-6ade-02fa-b77e-6f0e33f230ac%40gmail.com.
Nevin Liber
2017-10-15 18:06:06 UTC
Permalink
On Sun, Oct 15, 2017 at 11:27 AM, Jonathan MÃŒller <
However, for a hypothetical redesign of the standard library I think this
should be considered.
An allocator interface similar to the simple PMRs as opposed to the
complexity mess that is Allocator, but instead of virtual functions and
class hierarchy it is "concept-based" like Allocator and provides the
possibility of using type erasure.
How do you implement a type-erased version of Allocator which can call
construct? Please provide a link to working code.

Unfortunately, allocators have two jobs: (1) allocate & release memory and
(2) construct and destroy objects. They also have a major drawback in that
they have to be part of the type system (eg, vector<T, A<T>> and vector<T,
B<T>> are different types).

PMRs are about addressing (1) in such a way that using a different
allocation scheme does not require a different type family (eg, vector<T,
polymorphic_allocator<T>> is the type no matter what allocation scheme is
used).

If you or the OP has a better way to solve this with the above constraints,
present it. If you have different constraints, well, that would be a
different proposal and it is a disservice to say that PMRs do not meet that
need because PMRs are not trying to meet that need.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BPQ9w4Yc%3DLHQ8eTCSzha5dakSgeuvp91HA1QUc2h-sHxQ%40mail.gmail.com.
Jonathan Müller
2017-10-15 19:12:53 UTC
Permalink
On Sun, Oct 15, 2017 at 11:27 AM, Jonathan Müller
However, for a hypothetical redesign of the standard library I think
this should be considered.
An allocator interface similar to the simple PMRs as opposed to the
complexity mess that is Allocator, but instead of virtual functions
and class hierarchy it is "concept-based" like Allocator and
provides the possibility of using type erasure.
How do you implement a type-erased version of Allocator which can call
construct?  Please provide a link to working code.
You don't. I'm deliberately saying an allocator similar to PMRs - it
doesn't care about any type T, construction/destruction like the
Allocator do.
Unfortunately, allocators have two jobs:  (1) allocate & release memory
and (2) construct and destroy objects.  They also have a major drawback
in that they have to be part of the type system (eg, vector<T, A<T>> and
vector<T, B<T>> are different types).
PMRs are about addressing (1) in such a way that using a different
allocation scheme does not require a different type family (eg,
vector<T, polymorphic_allocator<T>> is the type no matter what
allocation scheme is used).
I'm aware.
If you or the OP has a better way to solve this with the above
constraints, present it.  If you have different constraints, well, that
would be a different proposal and it is a disservice to say that PMRs do
not meet that need because PMRs are not trying to meet that need.
All I'm saying is: there are very special cases where the PMRs
implementation with virtual functions has unnecessary overhead.
So it would be great if there was a concept like PMRs but without the
need for virtual functions.

Like the OP described: You simply write a class that provides the same
interface and can then plug that into a container.

Yes, we already have that facility - Allocator - but Allocator also does
construction/destruction and is such unnecessary complex for controlling
memory allocation.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/70e6aa0d-f41a-440c-f0ef-4969535d08ed%40gmail.com.
Nicol Bolas
2017-10-15 22:59:29 UTC
Permalink
Post by Nevin Liber
However, for a hypothetical redesign of the standard library I think this
should be considered.
An allocator interface similar to the simple PMRs as opposed to the
complexity mess that is Allocator, but instead of virtual functions and
class hierarchy it is "concept-based" like Allocator and provides the
possibility of using type erasure.
How do you implement a type-erased version of Allocator which can call
construct? Please provide a link to working code.
Unfortunately, allocators have two jobs: (1) allocate & release memory
and (2) construct and destroy objects. They also have a major drawback in
that they have to be part of the type system (eg, vector<T, A<T>> and
vector<T, B<T>> are different types).
That's kind of the issue. With the current container design, everything you
say is right.

But once we're in the realm of a "hypothetical redesign of the standard
library", we should not assume that this is the way things *have to be*. Indeed,
the whole point of redesigning the system is to fix all those things that
cannot be fixed without a redesign. And the allocator system ought to be on
the short list for reconsideration under such circumstances.

In my view, an allocator should not be dealing in the construction and
destruction of objects at all. As I understand things, this was done in
part to allow for specialized allocator reference/pointer types, which is a
thing that didn't really work and is essentially defunct. What containers
should get is a raw memory allocator, not an object construction system.

It's the container themselves that should deal in construction/destruction
of the object, via placement new/delete.

And I know that this can cause problems when you want to call private
constructors (you can make allocators friends of the type). But that's a
problem for *any* system when you want to indirectly construct an object.
`make_shared/unique` has this problem, since it doesn't take an allocator.
The solution users should use is private key types, not by making
construction part of the allocator.

Having the allocator be part of the type system is fine, so long as there's
a mechanism to choose to have a runtime-defined allocator (as we do now
with PMR).

PMRs are about addressing (1) in such a way that using a different
Post by Nevin Liber
allocation scheme does not require a different type family (eg, vector<T,
polymorphic_allocator<T>> is the type no matter what allocation scheme is
used).
If you or the OP has a better way to solve this with the above
constraints, present it. If you have different constraints, well, that
would be a different proposal and it is a disservice to say that PMRs do
not meet that need because PMRs are not trying to meet that need.
--
+1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e037028d-63f2-44cb-91cf-c4a889e88694%40isocpp.org.
Arthur O'Dwyer
2017-10-15 22:04:26 UTC
Permalink
Post by Mingxin Wang
I think `std::pmr::memory_resource` is very useful in polymorphic
programming. However, the design of `std::pmr::memory_resource` seems not
1. it could not be efficient enough in some cases where there is no demand
for polymorphism in memory allocation, and
2. it could not be efficient enough when calling the member function
`is_equal`, as the function seems not implementable without RTTI.
I think it is more reasonable to make `std::pmr::memory_resource` a
"polymorphic wrapper" that could be constructible from any concrete memory
resource, like `std::function` could be constructible from callable types,
so that the issues above could be handled.
What you are describing has already been implemented (and proposed, AFAIK).
It is an excellent idea. I don't know why it didn't get into C++17 along
with all the other pieces of PMR that did.
http://en.cppreference.com/w/cpp/experimental/resource_adaptor

However, PMR will never be all things to all people. You should certainly
read my paper P0773R0 "Towards Meaningful Fancy Pointers"
<http://quuxplusone.github.io/draft/fancy-pointers.html> in the upcoming
pre-Albuquerque mailing; it will clarify your thinking about "what is an
allocator" and especially "what does allocator equality mean"; and it will
make you much more pessimistic about the chances of shoehorning the full
C++11 allocator model (including fancy pointer types) into type-erased
things like polymorphic_allocator, function, and any.

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/16a45cfa-8016-4f9d-8d6c-48d1e73c4874%40isocpp.org.
Mingxin Wang
2017-10-16 09:24:01 UTC
Permalink
Post by Arthur O'Dwyer
What you are describing has already been implemented (and proposed,
AFAIK). It is an excellent idea. I don't know why it didn't get into C++17
along with all the other pieces of PMR that did.
http://en.cppreference.com/w/cpp/experimental/resource_adaptor
<http://www.google.com/url?q=http%3A%2F%2Fen.cppreference.com%2Fw%2Fcpp%2Fexperimental%2Fresource_adaptor&sa=D&sntz=1&usg=AFQjCNGbUH6FrHld29Itv0z9YvGaubul7Q>
`std::experimental::pmr::resource_adaptor` seems not what I have suggested.
I think "MemoryResource" has lower level semantics (provides more basic
service) than "Allocator" does, and therefore "Allocator" could be
"MemoryResource"'s user. On the contrary, defining a basic service with an
advanced one seems logically unreasonable, so I do not suggest
`std::experimental::pmr::resource_adaptor` should be included in the
standard.

However, PMR will never be all things to all people. You should certainly
Post by Arthur O'Dwyer
read my paper P0773R0 "Towards Meaningful Fancy Pointers"
<http://quuxplusone.github.io/draft/fancy-pointers.html> in the upcoming
pre-Albuquerque mailing; it will clarify your thinking about "what is an
allocator" and especially "what does allocator equality mean"; and it will
make you much more pessimistic about the chances of shoehorning the full
C++11 allocator model (including fancy pointer types) into type-erased
things like polymorphic_allocator, function, and any.
Thank you for the paper! However, I am targeting at neither "Allocator" nor
"the full C++11 allocator model". The target in this discussion is the PMR
system.

Mingxin Wang
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bb3ae52c-c3be-4966-a8d5-731b6f68919b%40isocpp.org.
Loading...