[interprocess] container requirements for B.IP allocators

In the section entitled "Container requirements for Boost.Interprocess allocators" from Boost.Interprocess documentation (http://tinyurl.com/2bruhb ), it is stated that containers compatible with B.IP allocators must honor the following conditions: 1. STL containers may not assume that memory allocated with an allocator can be deallocated with other allocators of the same type [...] 2. Containers' internal pointers should be of the type allocator::pointer and containers may not assume allocator::pointer is a raw pointer. 3. All objects must be constructed-destroyed via allocator::construct and allocator::destroy functions. I see two problems with requirement 3: a. Chances are that allocator member functions construct, destroy and address be deprecated in C++0x, if the N2257 paper by Matt Austern (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2257.html ) is taken into consideration. b. I've casually inspected the code of B.IP and seems like the allocator construct/destroy interface is *not* actually used, see for instance list_alloc in boost/interprocess/containers/list.hpp So, if condition 3 is not used, I wonder what the actual requirements are with respect to allocator::pointer. I guess the main assumption B.IP containers work on is that allocator::pointer is implicitly convertible to/from T*. Also, what the requirements are when handling (nonraw) pointers to types other than T? Consider the following: struct T { X x; }; If I've got an allocator::pointer pt to an object t of type T and want to store a nonraw pointer to x, how should I do? I guess something like: allocator::rebind<X>::other::pointer px=pt->x; but this is just a guess from my part, as it's not documented. Some clarifications on these issues, and maybe a rewriting of the relevant doc sections, would be welcome for those container writers willing to become B.IP allocator compatible. Thank you! Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Hi Joaquín, Joaquín Mª López Muñoz wrote:
In the section entitled "Container requirements for Boost.Interprocess allocators" from Boost.Interprocess documentation (http://tinyurl.com/2bruhb ), it is stated that containers compatible with B.IP allocators must honor the following conditions:
[snip]
I see two problems with requirement 3:
a. Chances are that allocator member functions construct, destroy and address be deprecated in C++0x, if the N2257 paper by Matt Austern (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2257.html ) is taken into consideration. b. I've casually inspected the code of B.IP and seems like the allocator construct/destroy interface is *not* actually used, see for instance list_alloc in boost/interprocess/containers/list.hpp
This was recently changed due to the restrictive nature of construct() (only copy construction, :-( ). I returned to placement new construction. Some committee members kindly requested my opinion when Matt's paper appeared and I just said that it was possible to use Interprocess without construct and destroy as long as the smart pointer is generically convertible (say, using ADL friendly get_pointer or requesting smart pointers to have a .get() member function to extract the raw pointer. The smart pointer is required to be constructible also from pointers to avoid also calling "address()" and other unuseful functions. Documentation of Boost.Interprocess is outdated and should reflect the new requirements.
So, if condition 3 is not used, I wonder what the actual requirements are with respect to allocator::pointer. I guess the main assumption B.IP containers work on is that allocator::pointer is implicitly convertible to/from T*.
Well, offset_ptr is not implicitly convertible to T* but is constructible (no explicit constructor) from a raw pointer. Implicit convertibility to a raw pointer was a major source of problems (you know that conversion operators are really tricky). Now the only requirement for the container is to use a generic function to extract the raw pointer. I've chosen to follow shared_ptr interface: --> A smart pointer has a function called "get()" to extract the raw pointer. --> A smart pointer defines a function called "get_pointer()" to do the same that can be found with ADL. In theory, the ADL option is appropriate, but in practice ADL is quite a big nightmare for some compilers. Well, that's because I don't really understand ADL ;-) So the requirements for B.IP allocator::pointer are the same for those for B.Intrusive (which are not well explained): http://tinyurl.com/2cjvr7
Also, what the requirements are when handling (nonraw) pointers to types other than T? Consider the following:
struct T { X x; };
If I've got an allocator::pointer pt to an object t of type T and want to store a nonraw pointer to x, how should I do? I guess something like:
allocator::rebind<X>::other::pointer px=pt->x;
You could use rebind and also request compatibility with Boost pointer_to_other<> utility. pointer_to_other is just more convenient to write and independent from the allocator. Although in theory making the pointer convertible from/to a raw pointer might seem restrictive for some theoretical applications of allocators (the famous but not yet implemented containers allocating nodes on disk), in practice I haven't seen such restrictions. For example, can std::vector use std::copy to copy elements pointed by an smart pointer when reallocating the buffer? Or it should destroy the elements with allocator::destroy and construct them again with allocator::construct? My choice is to bring allocator::pointer requirements down to the earth. Convertibility to raw pointers also offers the advantage of using static_cast which are essential when trying to optimize node containers.
but this is just a guess from my part, as it's not documented. Some clarifications on these issues, and maybe a rewriting of the relevant doc sections, would be welcome for those container writers willing to become B.IP allocator compatible.
I completely agree. I just was not expecting someone to write B.IP compatible containers yet and this was on my low priority queue. Are you trying to make B.MI compatible B.IP? That's going to make some programmers really happy ;-)
Thank you!
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
Regards, Ion

Ion Gaztañaga ha escrito:
Hi Joaquín,
Joaquín Mª López Muñoz wrote:
[...]
b. I've casually inspected the code of B.IP and seems like the allocator construct/destroy interface is *not* actually used, see for instance list_alloc in boost/interprocess/containers/list.hpp
This was recently changed due to the restrictive nature of construct() (only copy construction, :-( ). I returned to placement new construction. Some committee members kindly requested my opinion when Matt's paper appeared and I just said that it was possible to use Interprocess without construct and destroy as long as the smart pointer is generically convertible (say, using ADL friendly get_pointer or requesting smart pointers to have a .get() member function to extract the raw pointer. The smart pointer is required to be constructible also from pointers to avoid also calling "address()" and other unuseful functions. Documentation of Boost.Interprocess is outdated and should reflect the new requirements.
So, if condition 3 is not used, I wonder what the actual requirements are with respect to allocator::pointer. I guess the main assumption B.IP containers work on is that allocator::pointer is implicitly convertible to/from T*.
Well, offset_ptr is not implicitly convertible to T* but is constructible (no explicit constructor) from a raw pointer. Implicit convertibility to a raw pointer was a major source of problems (you know that conversion operators are really tricky). Now the only requirement for the container is to use a generic function to extract the raw pointer. I've chosen to follow shared_ptr interface:
--> A smart pointer has a function called "get()" to extract the raw pointer. --> A smart pointer defines a function called "get_pointer()" to do the same that can be found with ADL.
Why this hassle? Cannot you just use: T* raw=&*p; ?? This looks *far* more generic than requiring compliant smart pointers to go with get or get_pointer.
In theory, the ADL option is appropriate, but in practice ADL is quite a big nightmare for some compilers. Well, that's because I don't really understand ADL ;-)
So the requirements for B.IP allocator::pointer are the same for those for B.Intrusive (which are not well explained):
I guess these should also be corrected, since one of the requirements reads: "It must be convertible to a raw pointer and constructible from a raw pointer." which as you just stated is not what's actually assumed. Also, I can't finally figure out if you're requiring *implicit* or *explicit* construction from T*.
Also, what the requirements are when handling (nonraw) pointers to types other than T? Consider the following:
struct T { X x; };
If I've got an allocator::pointer pt to an object t of type T and want to store a nonraw pointer to x, how should I do? I guess something like:
allocator::rebind<X>::other::pointer px=pt->x;
You could use rebind and also request compatibility with Boost pointer_to_other<> utility. pointer_to_other is just more convenient to write and independent from the allocator.
Both techniques are not equivalent... Nothing prevents one from providing, for instance, an allocator family whose pointer typedef is raw for some types an belongs to a class template X<...> for some other, thus breaking the pointer_to_other<> utility. If I were to standardize what kind of exotic allocators STL might support, I'd go with rebind.
Although in theory making the pointer convertible from/to a raw pointer might seem restrictive for some theoretical applications of allocators (the famous but not yet implemented containers allocating nodes on disk), in practice I haven't seen such restrictions. For example, can std::vector use std::copy to copy elements pointed by an smart pointer when reallocating the buffer? Or it should destroy the elements with allocator::destroy and construct them again with allocator::construct?
My choice is to bring allocator::pointer requirements down to the earth. Convertibility to raw pointers also offers the advantage of using static_cast which are essential when trying to optimize node containers.
but this is just a guess from my part, as it's not documented. Some clarifications on these issues, and maybe a rewriting of the relevant doc sections, would be welcome for those container writers willing to become B.IP allocator compatible.
I completely agree. I just was not expecting someone to write B.IP compatible containers yet and this was on my low priority queue.
If you need a partenaire to discuss the issue with, please consider me a volunteer. As it currently stands (see N2284), the standard does not provide any clue as to what kind of allocators other than the regular ones STL implementors are encouraged to support (it merely states that these non-regular allocators might "encapsulate more general memory models and [...] support non-equal instances.") It'd be nice if we can come up with a concrete proposal to add to this encouragement section --otherwise it's almost impossible that future STL implementations might ever support B.IP!!
Are you trying to make B.MI compatible B.IP? That's going to make some programmers really happy ;-)
I'm beginning to survey the terrain :) Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Joaquín Mª López Muñoz wrote:
[snip]
--> A smart pointer has a function called "get()" to extract the raw pointer. --> A smart pointer defines a function called "get_pointer()" to do the same that can be found with ADL.
Why this hassle? Cannot you just use:
T* raw=&*p;
!!! Yes, much simple. Unless operator & is overloaded. Since we can't produce smart references, &* should lead to a raw pointer. But if someday we can produce smart references this syntax is maybe dangerous. But I'm not saying this should not work.
?? This looks *far* more generic than requiring compliant smart pointers to go with get or get_pointer.
I agree.
I guess these should also be corrected, since one of the requirements reads:
"It must be convertible to a raw pointer and constructible from a raw pointer."
which as you just stated is not what's actually assumed. Also, I can't finally figure out if you're requiring *implicit* or *explicit* construction from T*.
Ok. I agree that the definition is pretty bad.
allocator::rebind<X>::other::pointer px=pt->x; You could use rebind and also request compatibility with Boost pointer_to_other<> utility. pointer_to_other is just more convenient to write and independent from the allocator.
Both techniques are not equivalent... Nothing prevents one from providing, for instance, an allocator family whose pointer typedef is raw for some types an belongs to a class template X<...> for some other, thus breaking the pointer_to_other<> utility. If I were to standardize what kind of exotic allocators STL might support, I'd go with rebind.
If an allocator with value_type = T defines pointer as T* and another allocator with value_type = U defines the pointer type as an smart pointer, then we could not build node containers because we really construct pointer of the node type and not pointer of the value type. I think it's realistic and simpler to require that allocator<X>::rebind<Y>::other::pointer should be pointer_to_other<X::pointer, Y>::type
I completely agree. I just was not expecting someone to write B.IP compatible containers yet and this was on my low priority queue.
If you need a partenaire to discuss the issue with, please consider me a volunteer. As it currently stands (see N2284), the standard does not provide any clue as to what kind of allocators other than the regular ones STL implementors are encouraged to support (it merely states that these non-regular allocators might "encapsulate more general memory models and [...] support non-equal instances.") It'd be nice if we can come up with a concrete proposal to add to this encouragement section --otherwise it's almost impossible that future STL implementations might ever support B.IP!!
I've lost any hope for that ;-) Now, seriously, it would be nice to write a concrete proposal with other boost members to add support for advanced allocators to the standard. But when I proposed supporting advanced allocator::pointer types to the committee I didn't get much support. Maybe if you could make Boost.MultiIndex compatible with B.IP... ;-) Regards, Ion

I've lost any hope for that ;-) Now, seriously, it would be nice to write a concrete proposal with other boost members to add support for advanced allocators to the standard. But when I proposed supporting advanced allocator::pointer types to the committee I didn't get much support. Maybe if you could make Boost.MultiIndex compatible with B.IP... ;-)
If Boost.MultiIndex is compatible with B.IP then Boost.Bimap will be compatible too. :) Regards Matias

----- Mensaje original ----- De: Ion Gaztañaga <igaztanaga@gmail.com> Fecha: Lunes, Junio 4, 2007 9:17 pm Asunto: Re: [boost] [interprocess] container requirements for B.IP allocators Para: boost@lists.boost.org
Joaquín Mª López Muñoz wrote: [...]
Why this hassle? Cannot you just use:
T* raw=&*p;
!!! Yes, much simple. Unless operator & is overloaded. Since we can't produce smart references, &* should lead to a raw pointer. But if someday we can produce smart references this syntax is maybe dangerous. But I'm not saying this should not work.
I haven' thought much about it, but supporting smart references looks like something beyond the possibilities of STL-like containers. Just too much depends on references being true references. For one, the standard *mandates* that allocator::reference be T&. [...]
You could use rebind and also request compatibility with Boost pointer_to_other<> utility. pointer_to_other is just more convenient to write and independent from the allocator.
Both techniques are not equivalent... Nothing prevents one from providing, for instance, an allocator family whose pointer typedef is raw for some types an belongs to a class template X<...> for some other, thus breaking the pointer_to_other<> utility. If I were to standardize what kind of exotic allocators STL might support, I'd go with rebind.
If an allocator with value_type = T defines pointer as T* and another allocator with value_type = U defines the pointer type as an smart pointer, then we could not build node containers because we really construct pointer of the node type and not pointer of the value type. I think it's realistic and simpler to require that
allocator<X>::rebind<Y>::other::pointer
should be
pointer_to_other<X::pointer, Y>::type
I agree with you this assumption is reasonable enough. My point was merely that rebind is not guaranteed to be equivalent to pointer_to_other, even for legal situations. Let me propose another counterexample: template<typename T> class my_allocator { typedef my_smart_pointer<T> pointer; ... } // out of malevolence I specialize as follows class my_smart_pointer_to_char { // same interface as my_smart_pointer<char> } template<> class my_allocator<char> { my_smart_pointer_to_char pointer; ... }; This is legal, consistent, and breaks pointer_to_other. It is incredible silly, also:) but this was not my point. [...]
I've lost any hope for that ;-) Now, seriously, it would be nice to write a concrete proposal with other boost members to add support for advanced allocators to the standard. But when I proposed supporting advanced allocator::pointer types to the committee I didn't get much support. Maybe if you could make Boost.MultiIndex compatible with B.IP... ;-)
I don't see how this would lend support to the proposal in front of the committee, as B.MI is not a standard lib. But I think within B.IP allocator requirements lies a generic, perfectly neutral specification for (a form of) non-regular allocator support with its own merit outside the context of B.IP itself. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

JOAQUIN LOPEZ MU?Z wrote:
I haven' thought much about it, but supporting smart references looks like something beyond the possibilities of STL-like containers. Just too much depends on references being true references. For one, the standard *mandates* that allocator::reference be T&.
Yes, but proposals to overload operator.() have been submitted and even if they are not admitted for C++0x I'm pretty sure this issue will be requested again. I agree that the standard mandates allocator::reference be T&, but now if we try to make allocator::pointer different from T*, maybe allocator::reference should be relaxed. But this is not a very important point IMHO, I can live with any reasonable solution.
I agree with you this assumption is reasonable enough. My point was merely that rebind is not guaranteed to be equivalent to pointer_to_other, even for legal situations. Let me propose another counterexample:
template<typename T> class my_allocator { typedef my_smart_pointer<T> pointer; ... }
// out of malevolence I specialize as follows
class my_smart_pointer_to_char { // same interface as my_smart_pointer<char> } template<> class my_allocator<char> { my_smart_pointer_to_char pointer; ... };
This is legal, consistent, and breaks pointer_to_other. It is incredible silly, also:) but this was not my point.
Ok, it's legal. Now the standard says implementations can suppose that that allocator::pointer is T*, so if we are proposing a new allocator proposal we can require allocator<Y>::pointer to be the same as pointer_to_other<allocator<X>::pointer, Y>::type. My opinion is that if there is no reasonable use case to allow different smart pointers in in different allocator template specializations, we should request this limitation. This would allow, for example, building non-intrusive containers on top of intrusive containers (intrusive containers have no allocator, so they can use pointer_to_other<>), which is a good reason to request compatibility.
[...]
committee I didn't get much support. Maybe if you could make Boost.MultiIndex compatible with B.IP... ;-)
I don't see how this would lend support to the proposal in front of the committee, as B.MI is not a standard lib. But
B.IP is a rare, maniac-made silly library ;-) B.MI is widely used and it's the base for other nice libraries like Bimap (and I hope more libraries in the future). Adding B.MI to the list of advanced allocator friendly libraries would bring "existing practice" to another level. If we can add B.Intrusive also to the equation (making pointer_to_other<> compatible with allocator::pointer, for example) I think we have a solid ground to succeed. Committee members from Boost can do the rest ;-) Regards, Ion
participants (4)
-
"JOAQUIN LOPEZ MU?Z"
-
Ion Gaztañaga
-
Joaquín Mª López Muñoz
-
Matias Capeletto