Skip to content

Commit

Permalink
Specify constexpr in GC allocators if C++20 or later
Browse files Browse the repository at this point in the history
PR #603 (bdwgc).

This adds the `constexpr` attribute on each allocator member function,
including ctors and dtors, to enable easier use of these allocators in
compile-time execution.  A practical example of this would be a modern
C++ string class which supports constexpr construction, usage, and
destruction.  Without this change, the C++ compiler will not allow
such a class.

* include/gc/gc_allocator.h (gc_allocator, traceable_allocator,
gc_allocator_ignore_off_page): Declare all member functions as
GC_CONSTEXPR; reformat code.
* include/gc/gc_config_macros.h [__cplusplus && !GC_CONSTEXPR]
(GC_CONSTEXPR): Define macro.
  • Loading branch information
jeaye authored and ivmai committed Dec 29, 2023
1 parent 8c75a7c commit 9702965
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 63 deletions.
140 changes: 77 additions & 63 deletions include/gc/gc_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,34 +138,40 @@ class gc_allocator {
typedef gc_allocator<GC_Tp1> other;
};

gc_allocator() GC_NOEXCEPT {}
gc_allocator(const gc_allocator&) GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator() GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator(const gc_allocator&) GC_NOEXCEPT {}
# ifndef GC_NO_MEMBER_TEMPLATES
template <class GC_Tp1> GC_ATTR_EXPLICIT
gc_allocator(const gc_allocator<GC_Tp1>&) GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator(const gc_allocator<GC_Tp1>&) GC_NOEXCEPT {}
# endif
~gc_allocator() GC_NOEXCEPT {}
GC_CONSTEXPR ~gc_allocator() GC_NOEXCEPT {}

pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; }
GC_CONSTEXPR const_pointer address(const_reference GC_x) const {
return &GC_x;
}

// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0) {
GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) {
GC_type_traits<GC_Tp> traits;
return static_cast<GC_Tp *>(GC_selective_alloc(GC_n * sizeof(GC_Tp),
traits.GC_is_ptr_free,
false));
traits.GC_is_ptr_free, false));
}

GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT {
GC_FREE(__p);
}

void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT
{ GC_FREE(__p); }
GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT {
return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp);
}

size_type max_size() const GC_NOEXCEPT
{ return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp); }
GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) {
new(__p) GC_Tp(__val);
}

void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); }
};

template<>
Expand All @@ -183,16 +189,14 @@ class gc_allocator<void> {
};

template <class GC_T1, class GC_T2>
inline bool operator==(const gc_allocator<GC_T1>&,
const gc_allocator<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator==(const gc_allocator<GC_T1>&,
const gc_allocator<GC_T2>&) GC_NOEXCEPT {
return true;
}

template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_allocator<GC_T1>&,
const gc_allocator<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator!=(const gc_allocator<GC_T1>&,
const gc_allocator<GC_T2>&) GC_NOEXCEPT {
return false;
}

Expand All @@ -212,36 +216,42 @@ class gc_allocator_ignore_off_page {
typedef gc_allocator_ignore_off_page<GC_Tp1> other;
};

gc_allocator_ignore_off_page() GC_NOEXCEPT {}
gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&)
GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator_ignore_off_page() GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator_ignore_off_page(
const gc_allocator_ignore_off_page&) GC_NOEXCEPT {}
# ifndef GC_NO_MEMBER_TEMPLATES
template <class GC_Tp1> GC_ATTR_EXPLICIT
gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page<GC_Tp1>&)
GC_NOEXCEPT {}
GC_CONSTEXPR gc_allocator_ignore_off_page(
const gc_allocator_ignore_off_page<GC_Tp1>&) GC_NOEXCEPT {}
# endif
~gc_allocator_ignore_off_page() GC_NOEXCEPT {}
GC_CONSTEXPR ~gc_allocator_ignore_off_page() GC_NOEXCEPT {}

pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; }
GC_CONSTEXPR const_pointer address(const_reference GC_x) const {
return &GC_x;
}

// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0) {
GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) {
GC_type_traits<GC_Tp> traits;
return static_cast<GC_Tp *>(GC_selective_alloc(GC_n * sizeof(GC_Tp),
traits.GC_is_ptr_free,
true));
traits.GC_is_ptr_free, true));
}

void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT
{ GC_FREE(__p); }
GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT {
GC_FREE(__p);
}

size_type max_size() const GC_NOEXCEPT
{ return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp); }
GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT {
return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp);
}

void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) {
new(__p) GC_Tp(__val);
}

GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); }
};

template<>
Expand All @@ -259,16 +269,14 @@ class gc_allocator_ignore_off_page<void> {
};

template <class GC_T1, class GC_T2>
inline bool operator==(const gc_allocator_ignore_off_page<GC_T1>&,
const gc_allocator_ignore_off_page<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator==(const gc_allocator_ignore_off_page<GC_T1>&,
const gc_allocator_ignore_off_page<GC_T2>&) GC_NOEXCEPT {
return true;
}

template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_allocator_ignore_off_page<GC_T1>&,
const gc_allocator_ignore_off_page<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator!=(const gc_allocator_ignore_off_page<GC_T1>&,
const gc_allocator_ignore_off_page<GC_T2>&) GC_NOEXCEPT {
return false;
}

Expand All @@ -293,34 +301,42 @@ class traceable_allocator {
typedef traceable_allocator<GC_Tp1> other;
};

traceable_allocator() GC_NOEXCEPT {}
traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {}
GC_CONSTEXPR traceable_allocator() GC_NOEXCEPT {}
GC_CONSTEXPR traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {}
# ifndef GC_NO_MEMBER_TEMPLATES
template <class GC_Tp1> GC_ATTR_EXPLICIT
traceable_allocator(const traceable_allocator<GC_Tp1>&) GC_NOEXCEPT {}
GC_CONSTEXPR traceable_allocator(
const traceable_allocator<GC_Tp1>&) GC_NOEXCEPT {}
# endif
~traceable_allocator() GC_NOEXCEPT {}
GC_CONSTEXPR ~traceable_allocator() GC_NOEXCEPT {}

pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; }
GC_CONSTEXPR const_pointer address(const_reference GC_x) const {
return &GC_x;
}

// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0) {
GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) {
void * obj = GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp));
if (0 == obj)
GC_ALLOCATOR_THROW_OR_ABORT();
return static_cast<GC_Tp*>(obj);
}

void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT
{ GC_FREE(__p); }
GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT {
GC_FREE(__p);
}

GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT {
return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp);
}

size_type max_size() const GC_NOEXCEPT
{ return static_cast<GC_ALCTR_SIZE_T>(-1) / sizeof(GC_Tp); }
GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) {
new(__p) GC_Tp(__val);
}

void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); }
};

template<>
Expand All @@ -338,16 +354,14 @@ class traceable_allocator<void> {
};

template <class GC_T1, class GC_T2>
inline bool operator==(const traceable_allocator<GC_T1>&,
const traceable_allocator<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator==(const traceable_allocator<GC_T1>&,
const traceable_allocator<GC_T2>&) GC_NOEXCEPT {
return true;
}

template <class GC_T1, class GC_T2>
inline bool operator!=(const traceable_allocator<GC_T1>&,
const traceable_allocator<GC_T2>&) GC_NOEXCEPT
{
GC_CONSTEXPR inline bool operator!=(const traceable_allocator<GC_T1>&,
const traceable_allocator<GC_T2>&) GC_NOEXCEPT {
return false;
}

Expand Down
8 changes: 8 additions & 0 deletions include/gc/gc_config_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,14 @@
# endif
#endif

#ifndef GC_CONSTEXPR
# if __cplusplus >= 202002L
# define GC_CONSTEXPR constexpr
# else
# define GC_CONSTEXPR /* empty */
# endif
#endif

#endif /* __cplusplus */

#endif

0 comments on commit 9702965

Please sign in to comment.