From 07b6bd188f2395551fbd5eee3c3ca7ca3769bbbc Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 17 Sep 2020 17:15:42 +0200 Subject: Clean up and complete `` header This makes this header complete up to including C++14, except two exception classes that cannot be defined without ``. The functions related to the "new_handler" are declared but not actually defined, to prevent overhead and complexity. They are still declared to allow implementing them in user code if needed. This makes the implementation of all operator new and delete functions comply with the C++11/C++14 specification in terms of which should be actually implemented and which should be delegate to other functions. There are still some areas where these implementations are not entirely standards-compliant, which will be fixed in subsequent commits. This fixes part of #287 and fixes #47. --- cores/arduino/new.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'cores/arduino/new.cpp') diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp index fc30cf8..a36fd21 100644 --- a/cores/arduino/new.cpp +++ b/cores/arduino/new.cpp @@ -16,26 +16,63 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "new.h" -void *operator new(size_t size) { - return malloc(size); +namespace std { + const nothrow_t nothrow; } -void *operator new[](size_t size) { +void * operator new(size_t size) { return malloc(size); } +void * operator new[](size_t size) { + return operator new(size); +} -void * operator new(size_t size, void * ptr) noexcept { - (void)size; - return ptr; +void * operator new(size_t size, const std::nothrow_t tag) noexcept { + return operator new(size); +} +void * operator new[](size_t size, const std::nothrow_t& tag) noexcept { + return operator new[](size); } -void operator delete(void * ptr) { - free(ptr); +void * operator new(size_t size, void *place) noexcept { + // Nothing to do + (void)size; // unused + return place; +} +void * operator new[](size_t size, void *place) noexcept { + return operator new(size, place); } -void operator delete[](void * ptr) { +void operator delete(void * ptr) noexcept { free(ptr); } +void operator delete[](void * ptr) noexcept { + operator delete(ptr); +} + +#if __cplusplus >= 201402L +void operator delete(void* ptr, size_t size) noexcept { + operator delete(ptr); +} +void operator delete[](void * ptr, size_t size) noexcept { + operator delete[](ptr); +} +#endif // __cplusplus >= 201402L +void operator delete(void* ptr, const std::nothrow_t& tag) noexcept { + operator delete(ptr); +} +void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept { + operator delete[](ptr); +} + +void operator delete(void* ptr, void* place) noexcept { + (void)ptr; (void)place; // unused + // Nothing to do +} +void operator delete[](void* ptr, void* place) noexcept { + (void)ptr; (void)place; // unused + // Nothing to do +} -- cgit v1.2.3-18-g5258 From 6e0fb1ee25efa07be5aef320501f3908f44e5b79 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 17 Sep 2020 17:55:27 +0200 Subject: Make zero-sized new standards-compliant This fixes part of #287. --- cores/arduino/new.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'cores/arduino/new.cpp') diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp index a36fd21..1683594 100644 --- a/cores/arduino/new.cpp +++ b/cores/arduino/new.cpp @@ -23,6 +23,10 @@ namespace std { } void * operator new(size_t size) { + // Even zero-sized allocations should return a unique pointer, but + // malloc does not guarantee this + if (size == 0) + size = 1; return malloc(size); } void * operator new[](size_t size) { -- cgit v1.2.3-18-g5258 From 1a885ce890219213ac24c00d5aededa88c122fe1 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 17 Sep 2020 17:59:08 +0200 Subject: Optionally let new terminate on allocation failure This is currently disabled, keeping the old behavior of returning NULL on failure, but should probably be enabled in the future as code that does want to do a null check has had a chance to switch to the more portable nothrow versions. When enabled, allocation failure calls the weak `std::terminate()`, which calls `abort()` by default, but can be replaced by user code to do more specific handling. To enable this, a macro must be defined (in new.cpp or on the compiler commandline). This fixes part of #287. --- cores/arduino/new.cpp | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'cores/arduino/new.cpp') diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp index 1683594..19d80b6 100644 --- a/cores/arduino/new.cpp +++ b/cores/arduino/new.cpp @@ -18,26 +18,61 @@ #include "new.h" +// The C++ spec dicates that allocation failure should cause the +// (non-nothrow version of the) operator new to throw an exception. +// Since we expect to have exceptions disabled, it would be more +// appropriate (and probably standards-compliant) to terminate instead. +// Historically failure causes null to be returned, but this define +// allows switching to more robust terminating behaviour (that might +// become the default at some point in the future). Note that any code +// that wants null to be returned can (and should) use the nothrow +// versions of the new statement anyway and is unaffected by this. +// #define NEW_TERMINATES_ON_FAILURE + namespace std { + // Defined in abi.cpp + void terminate(); + const nothrow_t nothrow; } -void * operator new(size_t size) { +static void * new_helper(size_t size) { // Even zero-sized allocations should return a unique pointer, but // malloc does not guarantee this if (size == 0) size = 1; return malloc(size); } + +void * operator new(size_t size) { + void *res = new_helper(size); +#if defined(NEW_TERMINATES_ON_FAILURE) + if (!res) + std::terminate(); +#endif + return res; +} void * operator new[](size_t size) { return operator new(size); } void * operator new(size_t size, const std::nothrow_t tag) noexcept { +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else return operator new(size); +#endif } void * operator new[](size_t size, const std::nothrow_t& tag) noexcept { +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else return operator new[](size); +#endif } void * operator new(size_t size, void *place) noexcept { -- cgit v1.2.3-18-g5258 From 6d292502e138b5c73705f0df8095ded3f1df956f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 17 Sep 2020 19:43:09 +0200 Subject: Use std::size_t in new/delete The standard dictates that `std::size_t` is used, rather than the plain `size_t` type. Even though these types are usually, if not always, exactly the same type, other code might assume that `std::size_t` is actually used and thus also available under that name after including ``. This fixes that by using the right type. One challenge is that it is usually declared in headers that we do not have available, so this just defines the `std::size_t` type in the `` header to work around that. --- cores/arduino/new.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'cores/arduino/new.cpp') diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp index 19d80b6..9047b2d 100644 --- a/cores/arduino/new.cpp +++ b/cores/arduino/new.cpp @@ -36,7 +36,7 @@ namespace std { const nothrow_t nothrow; } -static void * new_helper(size_t size) { +static void * new_helper(std::size_t size) { // Even zero-sized allocations should return a unique pointer, but // malloc does not guarantee this if (size == 0) @@ -44,7 +44,7 @@ static void * new_helper(size_t size) { return malloc(size); } -void * operator new(size_t size) { +void * operator new(std::size_t size) { void *res = new_helper(size); #if defined(NEW_TERMINATES_ON_FAILURE) if (!res) @@ -52,11 +52,11 @@ void * operator new(size_t size) { #endif return res; } -void * operator new[](size_t size) { +void * operator new[](std::size_t size) { return operator new(size); } -void * operator new(size_t size, const std::nothrow_t tag) noexcept { +void * operator new(std::size_t size, const std::nothrow_t tag) noexcept { #if defined(NEW_TERMINATES_ON_FAILURE) // Cannot call throwing operator new as standard suggests, so call // new_helper directly then @@ -65,7 +65,7 @@ void * operator new(size_t size, const std::nothrow_t tag) noexcept { return operator new(size); #endif } -void * operator new[](size_t size, const std::nothrow_t& tag) noexcept { +void * operator new[](std::size_t size, const std::nothrow_t& tag) noexcept { #if defined(NEW_TERMINATES_ON_FAILURE) // Cannot call throwing operator new[] as standard suggests, so call // malloc directly then @@ -75,12 +75,12 @@ void * operator new[](size_t size, const std::nothrow_t& tag) noexcept { #endif } -void * operator new(size_t size, void *place) noexcept { +void * operator new(std::size_t size, void *place) noexcept { // Nothing to do (void)size; // unused return place; } -void * operator new[](size_t size, void *place) noexcept { +void * operator new[](std::size_t size, void *place) noexcept { return operator new(size, place); } @@ -92,10 +92,10 @@ void operator delete[](void * ptr) noexcept { } #if __cplusplus >= 201402L -void operator delete(void* ptr, size_t size) noexcept { +void operator delete(void* ptr, std::size_t size) noexcept { operator delete(ptr); } -void operator delete[](void * ptr, size_t size) noexcept { +void operator delete[](void * ptr, std::size_t size) noexcept { operator delete[](ptr); } #endif // __cplusplus >= 201402L -- cgit v1.2.3-18-g5258