I am reading Item 10 in Effective STL by Scott Meyers on allocators in C++.
Standard says that an implementation of the STL is permitted to assume
that all allocator objects of the same type are equivalent and always
compare equal.
That's all well and good, but the more you think about it. the more
you'll realize just how draconian a restriction it is that STL
implementations may assume that allocators of the same type are
equivalent. It means that portable allocator objects — allocators that
will function correctly under different STL implementations — may not
have state. Let's be explicit about this: it means that portable
allocators may not have any nonstatic data members, at least not any
that affect their behavior. None. Nada. That means, for example, you
can't have one SpecialAllocator that allocates from one heap and
a different SpecialAllocator that allocates from a different
heap. Such allocators wouldn't be equivalent, and STL implementations
exist where attempts to use both allocators could lead to corrupt
runtime data structures.
In fairness to the Standardization Committee, I should point out that
it included the following statement immediately after the text that
permits STL implementers to assume that allocators of the same type
are equivalent:
Implementors are encouraged to supply libraries that ... support
non-equal instances. In such implementations. ... the semantics of
containers and algorithms when allocator instances compare non-equal
are implementation-defined.
This is a lovely sentiment, but as a user of the STL who is
considering the development of a custom allocator with state, it
offers you next to nothing. You can take advantage of this statement
only if (1) you know that the STL implementations you are using
support inequivalent allocators, (2) you are willing to delve into
their documentation to determine whether the implementation-defined
behavior of "non-equal" allocators is acceptable to you, and
(3) you're not concerned about porting your code to STL
implementations that may take advantage of the latitude expressly
extended to them by the Standard. In short, this paragraph — paragraph
5 of section 20.1.5. for those who insist on knowing — is the
Standard's "1 have a dream" speech for allocators. Until that dream
becomes common reality, programmers concerned about portability will
limit themselves to custom allocators with no state.
My question on above paragraph are
What does author mean by inequivalent or non-equal allocators?
What does last paragraph in above text i.e, point 3 mean in simple terms?
That information is out of date. C++11 and later versions support stateful allocators.
The quotes you have posted from Effective C++ are only of concern if you are writing a C++ library which requires custom allocators, does not require C++11, and which supports building against unknown/unspecified standard libraries. To a first approximation, nobody is doing this anymore. The people who were doing it before often had their own "enhanced" standard library implementations to support stateful allocators, such as EASTL or BDESTL.
Two allocators should compare equal if memory allocated by one can be freed by the other. So, for example, an allocator object that allocates from a pool that it holds can allocate and free memory from that pool, but a different allocator object that has a different pool can't (without a great deal of extra bookkeeping) free memory allocated by the first object.
Making that work right was beyond what the standards committee wanted to take on when allocators were first introduced, which is why the words were so squishy. And that license has been revoked in more recent versions of the standard.
The last paragraph means that if you write an allocator whose objects rely on internal state (e.g., the pool allocator that I mentioned above), and the library you're using respects the equality operator (as in, it won't try to pass pointers around among allocators that don't compare equal), your code will break when you try to use it with a different implementation that doesn't pay attention to the equality operator.
Related
I am certain that, in practice, use of ::new is thread-safe. My question is what part of the standard provides that guarantee, if any? Is this a convention? Is this something where the standard gives implementations a lot of latitude (like the relatively loose constraints about what size each data type is) to support a wide variety of hardware?
I'm hoping that there's just a line in the C++11 standard somewhere that explicitly specifies "implementations of ::new must be thread-safe".
I'd also love to see some standardese about the thread-safety of operator new overloads. I imagine that they would also need to be required to be thread-safe, but these functions also do not fall under the blanket guarantee that const => thread safe (in C++11).
Thanks!
I believe this is implicitly guaranteed by the C++11 standard. If it were not, then usage of the operator new or new expression might cause a data race, and that would not be allowed by the standard. For reference, see §17.6.5.9 Data race avoidance and also
18.6.1.4 Data races [new.delete.dataraces]
"The library versions of operator new and operator delete, user replacement versions of global operator new and operator delete, and the C standard library functions calloc, malloc, realloc, and free shall not introduce data races (1.10) as a result of concurrent calls from different threads. Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order."
Your own overrides or your own replacements for the global operators should fulfill this requirement as well.
See also this proposal N3664 "Clarifying Memory Allocation", which puts more emphasis on that matter.
The C++ standard does not outright require that new be thread-safe. Some implementations explicitly support building C++ code in single-threaded mode, where the C standard library, including malloc() may not be thread-safe. The platforms most of us use every day do offer thread-safe allocation, of course.
Even if your platform provides a thread-safe new, you still need to be wary if you use any libraries which implement their own operator new, or if you do so yourself. It's certainly possible to write a new which works only in a single thread--maybe even intentionally!
I'm writing a custom container template class that, as many (if not all) the containers in the stl, can use a specified allocator type.
To implement the range insert function, I need to move some of the elements in the container a number of spaces forward, where the memory is still uninitialized. To do this I want to use some (nonexistent) version of std::uninitialized_move() that uses the allocator in the container.
The other option is to do the move-construction of the objects using the allocator in a for loop and destroy the constructed objects in case of an exception. That's basically re-implementing std::uninitialized_move() with an extra feature.
The standard library implementation for my compiler (GCC) has exactly the functions I need (std::__uninitialized_move_a(), std::__uninitialized_copy_a(), etc), and are in fact used in the implementation of std containers, but I think those are rather compiler-specific.
Should I use this functions (portability)? Or is there other, more practical, option?
Maybe there is something in the standard library that I'm missing.
Should I use this functions (portability)?
You shouldn't use the GCC internal functions.
No, there doesn't seem to be standard equivalents. You can write your own versions of those functions. If you do, note that CustomAlloc::construct is an optional function (for example, std::allocator doesn't have this function since C++20), so it should be use through std::allocator_traits<CustomAlloc>::construct. This has additional benefit of being constexpr since C++20.
Or is there other, more practical, option?
One option is to ignore the possibility that CustomAlloc::construct has been implemented to do something other than direct placement new, and thereby simply use the standard std::uninitialized_move.
This technically limits the allocators that your container supports in theory, but on the other hand I've never seen such custom allocator used in practice. This may be reasonable limitation if the container is for internal use at least. If you do this, document the behaviour carefully.
There are contexts in which we want our C++ code to not perform dynamic memory allocation ('on the heap'), specifically in some embedded development use cases.
There are standard library classes which can be implemented without dynamic memory allocation: optional, array, tuple, variant to name a few.
The same is true for standard library free functions.
Are there any such classes or functions which are guaranteed by the standard to not allocate memory dynamically? The only functions I could find with such a guarantee are the placement new() functions.
There are very few cases if any where the C++ standard makes any direct guarantee about not using dynamic memory.
On systems where dynamic memory allocation is signal-unsafe, you can be certain that all functions listed as signal-safe are non-allocating. The standard mentions
_Exit
abort
forward
initializer_list functions
memcpy
memmove
move
move_if_noexcept
numeric_limits members
quick_exit
signal
type traits
plain lock-free atomic operations
If you can assume conformance to another standard, POSIX, then it lists more functions that are async-signal-safe. Some of these functions listed by POSIX are provided by C++ (and C) standards as well (such as strcat), and therefore those standard C++ functions will be signal safe on all POSIX systems.
There are a few functions in [new.delete.placement], which are non-allocating by definition.
Another question separate from guarantees is, whether a reasonable implementation of a function or a type would not allocate. Many, many things such as std::tuple and std::array (with non-allocating type arguments naturally) fall into this category.
It would be reasonable that functions which are declared noexcept, and do not have any failure path (like setting error code, returning error indicating value, or terminating the process) shouldn't allocate, since allocation may throw.
Conversely, there are functions that in a reasonable implementation do allocate dynamic memory. Obviously those that involve allocators, as well as those listed in the SO post that you linked. One non-obvious one that often bites people writing signal handlers is missing from the list: It is not at all reasonable to expect printf or any of its related functions to not allocate.
What is "Per-object Data in Allocators". I can't seem to find what this means. Anyone have a good explanation or link for what this means in terms of the C++ Language?
CLARIFICATION
Section 19.4.2 "The C++ Programming Language (Special Edition)" pg. 573
"Similarly, if allocators were allowed to be perfectly general, the rebind mechanism that allows an allocator to allocate elements of arbitrary types would have to be more elaborate. Consequently, a standard allocator is assumed to hold no per-object data and an implementation of a standard container may take advantage of that."
Per-object data or local state refers to any non-static data members in the allocator class.
The issue is that currently (in c++03) there is no support for allocators with so-called local state. This is often considered to be a flaw with the allocator model in present-day c++.
Have a read through this article that details the design for a custom allocator. A paragraph under Design specifically addresses some of the pitfalls of allocators that incorporate local state.
Briefly, some operations in the standard library currently require that objects of a particular type be safely allocated by one instance of an allocator and deallocated by another instance of the allocator (both allocators are of the same type - of course!). This can be the case when implementing list::splice for instance. If allocators are allowed to have local state this can get tricky...
In the upcoming c++0x revision, it appears that allocators will be allowed to incorporate local state, check out the scoped allocator section here.
Hope this helps.
It simply means that std::allocator<T> does not contain any per-instance data members ... it is mainly a wrapper around memory allocation and deallocation functions, and also contains definitions of certain required typedefs, as well as mechanisms for rebinding an existing allocator so that it can allocate types that were not part of the original allocator template instantiation. So basically what is being stated is that if there were actual private data-members that had to be managed, especially in light of the requirement for a STL allocator to allow rebinding, that could greatly complicate the implementation of a generic allocator depending on what those per-instance data-members represented.
First of all, I don't think it is. But, I've observed such a behavior with MSVC 10.0 in Debug mode. I'm using a custom allocator class which relies on the user to pass only pointers allocated on the same instance to deallocate. However, in Release mode, my code is working.
Is this a bug or am I mistaken?
The standard requires that any allocator be able to deallocate memory produced by any other allocator of the same type, even if it's a totally different instance. This is required to get list::splice working correctly. It's largely considered a design flaw in the C++ spec, and in C++0x they're introducing a set of fixups to allocators to rememdy this. In the meantime, any allocator you use in the STL containers must not have its own local state.
EDIT: For those of you who want the original language on this, here's §20.1.5/4 of the C++ ISO spec:
Implementations of containers described in this International Standard are permitted to assume that their
Allocator template parameter meets the following two additional requirements beyond those in Table 32.
— All instances of a given allocator type are required to be interchangeable and always compare equal to
each other.
In the latest ISO draft of the C++0x standard, this requirement is no longer present. The default std::allocator will still maintain this invariant as required, but it doesn't look like you'll have to constrain yourself this way in the future.