Skip to content

fmt::detail::allocator is not constexpr aware in C++20. #4467

@phprus

Description

@phprus

malloc-based fmt::detail::allocator is not constexpr aware in C++20.

fmt/include/fmt/format.h

Lines 773 to 784 in 730fd4d

template <typename T> struct allocator : private std::decay<void> {
using value_type = T;
T* allocate(size_t n) {
FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
T* p = static_cast<T*>(malloc(n * sizeof(T)));
if (!p) FMT_THROW(std::bad_alloc());
return p;
}
void deallocate(T* p, size_t) { free(p); }
};

Without constexpr fmt::detail::allocator support for constexpr formatting will be limited because fmt::basic_memory_buffer depends on the fmt::detail::allocator.

fmt/include/fmt/format.h

Lines 807 to 844 in 730fd4d

template <typename T, size_t SIZE = inline_buffer_size,
typename Allocator = detail::allocator<T>>
class basic_memory_buffer : public detail::buffer<T> {
private:
T store_[SIZE];
// Don't inherit from Allocator to avoid generating type_info for it.
FMT_NO_UNIQUE_ADDRESS Allocator alloc_;
// Deallocate memory allocated by the buffer.
FMT_CONSTEXPR20 void deallocate() {
T* data = this->data();
if (data != store_) alloc_.deallocate(data, this->capacity());
}
static FMT_CONSTEXPR20 void grow(detail::buffer<T>& buf, size_t size) {
detail::abort_fuzzing_if(size > 5000);
auto& self = static_cast<basic_memory_buffer&>(buf);
const size_t max_size =
std::allocator_traits<Allocator>::max_size(self.alloc_);
size_t old_capacity = buf.capacity();
size_t new_capacity = old_capacity + old_capacity / 2;
if (size > new_capacity)
new_capacity = size;
else if (new_capacity > max_size)
new_capacity = max_of(size, max_size);
T* old_data = buf.data();
T* new_data = self.alloc_.allocate(new_capacity);
// Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
detail::assume(buf.size() <= new_capacity);
// The following code doesn't throw, so the raw pointer above doesn't leak.
memcpy(new_data, old_data, buf.size() * sizeof(T));
self.set(new_data, new_capacity);
// deallocate must not throw according to the standard, but even if it does,
// the buffer already uses the new storage and will deallocate it in
// destructor.
if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity);
}

This may also limit the functionality of PR #4456.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions