BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslma_memoryresourceimpsupport

Detailed Description

Provide support for implementing memory resources.

Outline

Purpose

Provide support for implementing memory resources

Classes

See also
bslma_memoryresource, bslma_allocator

Description

This component provides support functions and types for implementing a class derived from bsl::memory_resource. Currently, it provides a utility function, singularPointer, that provides a pointer suitable for an allocate function to return when a zero-sized allocation is requested.

Usage

This section illustrates intended use of this component.

Example 1: Write a memory_resource using singularPointer

In this example, we derive a NewDeleteResource class, derived from bsl::memory_resource, that allocates and deallocate using operator new and operator delete, respectively, and uses singularPointer() as a special value when allocating and allocating zero bytes.

First, we define the class interface, which implements the protected virtual functions do_allocate, do_deallocate, and do_is_equal:

#include <bsls_assert.h>
/// Memory resource that allocates using `operator new`.
class NewDeleteResource : public bsl::memory_resource {
protected:
// PROTECTED MANIPULATORS
/// Return the specified `size` bytes with the specified `alignment`
/// allocated from the global heap using `operator new`. The behavior
/// is undefined unless the specified `alignment` is less than or equal
/// to the maximum platform alignment.
void *do_allocate(std::size_t size, std::size_t alignment)
/// Deallocate the memory block specified by `p` having the specified
/// `size` and `alignment` from the global heap using `operator
/// delete`. The behavior is undefined unless `p` was returned from a
/// previous call to `allocate`, using the same `size` and `alignment`.
void do_deallocate(void *p, std::size_t size, std::size_t alignment)
/// Return `true` if `x` is a `NewDeleteResource` and `false`
/// otherwise.
bool do_is_equal(const bsl::memory_resource& x) const
};
Definition bslma_memoryresource.h:443
#define BSLS_KEYWORD_NOEXCEPT
Definition bsls_keyword.h:674
#define BSLS_KEYWORD_OVERRIDE
Definition bsls_keyword.h:695

Next, we implement the do_allocate method, which forwards most requests to operator new. Section [basic.stc.dynamic.allocation] of the C++ standard, however states that the return value of an allocation function when the requested size is zero is a non-null pointer to a suitably aligned block of storage, so we use singularPointer to provide the address in such circumstances:

#include <new> // `align_val_t` and aligned `new`
void *NewDeleteResource::do_allocate(std::size_t size,
std::size_t alignment)
{
if (0 == size) {
}
#ifdef __cpp_aligned_new
return ::operator new(size, std::align_val_t(alignment));
#else
(void) alignment;
return ::operator new(size);
#endif
}
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
static BSLS_KEYWORD_CONSTEXPR void * singularPointer()
Definition bslma_memoryresourceimpsupport.h:257
@ BSLS_MAX_ALIGNMENT
Definition bsls_alignmentutil.h:300

Next, we implement the do_deallocate method, which forwards most requests to operator delete, but does nothing if the incoming pointer was the result of a zero-sized allocation:

void NewDeleteResource::do_deallocate(void *p,
std::size_t size,
std::size_t alignment)
{
(void) size; // Not used in OPT build
BSLS_ASSERT(0 == size);
return;
}
BSLS_ASSERT(0 < size);
#ifdef __cpp_aligned_new
::operator delete(p, std::align_val_t(alignment));
#else
(void) (size, alignment);
::operator delete(p);
#endif
}

Next, we complete the implementation with do_is_equal. All instances of NewDeleteResource compare equal, so we need only check that the argument is a NewDeleteResource. As a short-cut, we check if the address of b is the same as this, to quickly catch the common case that they are both pointers to the singleton object:

bool NewDeleteResource::do_is_equal(const bsl::memory_resource& b) const
{
return this == &b || 0 != dynamic_cast<const NewDeleteResource *>(&b);
}

Now, when we call allocate, we observe that each non-zero allocation yields a different pointer, whereas all of the zero allocations yield the same address.

int main()
{
NewDeleteResource r;
void *p1 = r.allocate(1, 1);
void *p2 = r.allocate(8);
assert(p1 != p2);
void *p3 = r.allocate(0, 1);
void *p4 = r.allocate(0, 4);
assert(p3 != p1);
assert(p3 != p2);
assert(p3 == p4);

Finally, when we deallocate memory, we would expect nothing to happen when deallocating the zero-sized blocks p3 and p4:

r.deallocate(p1, 1, 1);
r.deallocate(p2, 8);
r.deallocate(p3, 0, 1);
r.deallocate(p4, 0, 4);
}