Skip to content

Commit aaa789e

Browse files
authored
Merge pull request #88 from 20162026/feature/noexceptions
noexceptions
2 parents 29d1105 + d5041e4 commit aaa789e

File tree

13 files changed

+295
-55
lines changed

13 files changed

+295
-55
lines changed

.github/workflows/ci_tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ jobs:
5353
args: "-DCMAKE_CXX_FLAGS=-fsanitize=thread"
5454
- description: "ASan"
5555
args: "-DCMAKE_CXX_FLAGS='-fsanitize=address -fsanitize=undefined'"
56+
- description: "NoExcep"
57+
args: "-DBEMAN_INPLACE_VECTOR_NO_EXCEPTIONS=on"
5658
include:
5759
- platform: ubuntu-24.04
5860
compiler:

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ option(
2727
${PROJECT_IS_TOP_LEVEL}
2828
)
2929

30+
option(
31+
BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
32+
"Disable exceptions by replacing throw calls with abort. Default: OFF. Values: { ON, OFF }."
33+
OFF
34+
)
35+
36+
configure_file(
37+
"${PROJECT_SOURCE_DIR}/include/beman/inplace_vector/config.hpp.in"
38+
"${PROJECT_BINARY_DIR}/include/beman/inplace_vector/config.hpp"
39+
@ONLY
40+
)
41+
3042
include(FetchContent)
3143
include(GNUInstallDirs)
3244

@@ -38,6 +50,7 @@ target_include_directories(
3850
beman.inplace_vector
3951
INTERFACE
4052
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
53+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
4154
$<INSTALL_INTERFACE:include>
4255
)
4356

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef BEMAN_INPLACE_VECTOR_CONFIG_HPP
2+
#define BEMAN_INPLACE_VECTOR_CONFIG_HPP
3+
4+
#cmakedefine01 BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
5+
6+
#endif

include/beman/inplace_vector/inplace_vector.hpp

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,38 @@
33
#ifndef BEMAN_INPLACE_VECTOR_INPLACE_VECTOR_HPP
44
#define BEMAN_INPLACE_VECTOR_INPLACE_VECTOR_HPP
55

6+
#if !defined(__has_include) || __has_include(<beman/inplace_vector/config.hpp>)
7+
#include <beman/inplace_vector/config.hpp>
8+
#endif
9+
610
#include <algorithm> // for rotate...
711
#include <array>
812
#include <compare>
913
#include <concepts> // for lots...
1014
#include <cstddef> // for size_t
1115
#include <cstdint> // for fixed-width integer types
1216
#include <cstdio> // for assertion diagnostics
13-
#include <cstdlib> // for abort
1417
#include <functional> // for less and equal_to
1518
#include <iterator> // for reverse_iterator and iterator traits
1619
#include <limits> // for numeric_limits
1720
#include <memory> // for destroy
1821
#include <new> // for operator new
1922
#include <ranges>
20-
#include <stdexcept> // for length_error
2123
#include <type_traits> // for aligned_storage and all meta-functions
2224

2325
// Artifact from previous implementation, can be used as hints for optimizer
2426
#define IV_EXPECT(EXPR)
2527

28+
#ifndef BEMAN_IV_THROW_OR_ABORT
29+
#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
30+
#include <cstdlib> // for abort
31+
#define BEMAN_IV_THROW_OR_ABORT(x) abort()
32+
#else
33+
#include <stdexcept> // for length_error
34+
#define BEMAN_IV_THROW_OR_ABORT(x) throw x
35+
#endif
36+
#endif
37+
2638
// beman::from_range_t
2739
namespace beman {
2840
struct from_range_t {};
@@ -260,8 +272,9 @@ struct inplace_vector
260272
static constexpr size_type max_size() noexcept { return N; }
261273
static constexpr size_type capacity() noexcept { return N; }
262274
constexpr void reserve(size_type n) {
263-
if (n > N) [[unlikely]]
264-
throw std::bad_alloc();
275+
if (n > N) [[unlikely]] {
276+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
277+
}
265278
}
266279
constexpr void shrink_to_fit() {}
267280

@@ -351,8 +364,9 @@ struct inplace_vector
351364
constexpr T &emplace_back(Args &&...args)
352365
requires(std::constructible_from<T, Args...>)
353366
{
354-
if (!try_emplace_back(std::forward<Args>(args)...)) [[unlikely]]
355-
throw std::bad_alloc();
367+
if (!try_emplace_back(std::forward<Args>(args)...)) [[unlikely]] {
368+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
369+
}
356370
return back();
357371
}
358372
constexpr T &push_back(const T &x)
@@ -395,12 +409,14 @@ struct inplace_vector
395409
requires(std::constructible_from<T, std::ranges::range_reference_t<R>>)
396410
{
397411
if constexpr (std::ranges::sized_range<R>) {
398-
if (size() + std::ranges::size(rg) > capacity()) [[unlikely]]
399-
throw std::bad_alloc();
412+
if (size() + std::ranges::size(rg) > capacity()) [[unlikely]] {
413+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
414+
}
400415
}
401416
for (auto &&e : rg) {
402-
if (size() == capacity()) [[unlikely]]
403-
throw std::bad_alloc();
417+
if (size() == capacity()) [[unlikely]] {
418+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
419+
}
404420
emplace_back(std::forward<decltype(e)>(e));
405421
}
406422
}
@@ -438,8 +454,9 @@ struct inplace_vector
438454
assert_iterator_in_range(position);
439455
if constexpr (std::random_access_iterator<InputIterator>) {
440456
if (size() + static_cast<size_type>(std::distance(first, last)) >
441-
capacity()) [[unlikely]]
442-
throw std::bad_alloc{};
457+
capacity()) [[unlikely]] {
458+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
459+
}
443460
}
444461
auto b = end();
445462
for (; first != last; ++first)
@@ -555,9 +572,9 @@ struct inplace_vector
555572
{
556573
if (sz == size())
557574
return;
558-
else if (sz > N) [[unlikely]]
559-
throw std::bad_alloc{};
560-
else if (sz > size())
575+
else if (sz > N) [[unlikely]] {
576+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
577+
} else if (sz > size())
561578
insert(end(), sz - size(), c);
562579
else {
563580
unsafe_destroy(begin() + sz, end());
@@ -569,25 +586,27 @@ struct inplace_vector
569586
{
570587
if (sz == size())
571588
return;
572-
else if (sz > N) [[unlikely]]
573-
throw std::bad_alloc{};
574-
else if (sz > size())
589+
else if (sz > N) [[unlikely]] {
590+
BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
591+
} else if (sz > size()) {
575592
while (size() != sz)
576593
emplace_back(T{});
577-
else {
594+
} else {
578595
unsafe_destroy(begin() + sz, end());
579596
unsafe_set_size(sz);
580597
}
581598
}
582599

583600
constexpr reference at(size_type pos) {
584-
if (pos >= size()) [[unlikely]]
585-
throw std::out_of_range("inplace_vector::at");
601+
if (pos >= size()) [[unlikely]] {
602+
BEMAN_IV_THROW_OR_ABORT(std::out_of_range("inplace_vector::at"));
603+
}
586604
return details::inplace_vector::index(*this, pos);
587605
}
588606
constexpr const_reference at(size_type pos) const {
589-
if (pos >= size()) [[unlikely]]
590-
throw std::out_of_range("inplace_vector::at");
607+
if (pos >= size()) [[unlikely]] {
608+
BEMAN_IV_THROW_OR_ABORT(std::out_of_range("inplace_vector::at"));
609+
}
591610
return details::inplace_vector::index(*this, pos);
592611
}
593612

@@ -606,7 +625,7 @@ struct inplace_vector
606625
std::copyable<T>)
607626
{
608627
for (auto &&e : x)
609-
emplace_back(e);
628+
unchecked_emplace_back(e);
610629
}
611630

612631
constexpr inplace_vector(inplace_vector &&x)
@@ -618,7 +637,7 @@ struct inplace_vector
618637
std::movable<T>)
619638
{
620639
for (auto &&e : x)
621-
emplace_back(std::move(e));
640+
unchecked_emplace_back(std::move(e));
622641
}
623642

624643
constexpr inplace_vector &operator=(const inplace_vector &x)
@@ -636,7 +655,7 @@ struct inplace_vector
636655
{
637656
clear();
638657
for (auto &&e : x)
639-
emplace_back(e);
658+
unchecked_emplace_back(e);
640659
return *this;
641660
}
642661

@@ -655,7 +674,7 @@ struct inplace_vector
655674
{
656675
clear();
657676
for (auto &&e : x)
658-
emplace_back(std::move(e));
677+
unchecked_emplace_back(std::move(e));
659678
return *this;
660679
}
661680

tests/beman/inplace_vector/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,21 @@ add_gtest(size_n_data)
4343
add_gtest(erasure)
4444
add_gtest(modifiers)
4545

46+
# only add noexception tests if NO_EXCEPTIONS option is set and compiler supports -fno-exceptions
47+
if(
48+
BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
49+
AND (
50+
CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
51+
OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
52+
)
53+
)
54+
add_gtest(noexceptions)
55+
target_compile_options(
56+
beman.inplace_vector.tests.noexceptions
57+
PRIVATE -fno-exceptions
58+
)
59+
endif()
60+
4661
# constexpr test
4762
add_executable(beman.inplace_vector.tests.constexpr constexpr.test.cpp)
4863
target_link_libraries(

tests/beman/inplace_vector/constructors.test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ TYPED_TEST(Constructors, SizedDefault) {
2121

2222
EXPECT_EQ(IV(0), IV{});
2323

24-
EXPECT_THROW(IV(IV::capacity() + 1), std::bad_alloc);
24+
SAFE_EXPECT_THROW(IV(IV::capacity() + 1), std::bad_alloc);
2525

2626
constexpr auto mid_size = std::midpoint(0ul, IV::capacity());
2727
IV mid(mid_size);
@@ -57,7 +57,7 @@ TYPED_TEST(Constructors, SizedValue) {
5757
IV device(0, value);
5858
EXPECT_EQ(device, IV{});
5959

60-
EXPECT_THROW(IV(IV::capacity() + 1, value), std::bad_alloc);
60+
SAFE_EXPECT_THROW(IV(IV::capacity() + 1, value), std::bad_alloc);
6161
}
6262

6363
if constexpr (IV::capacity() < 1u)

tests/beman/inplace_vector/container_requirements.test.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ TYPED_TEST(SequenceContainerRequirements, ConstructorInitializerList) {
626626
using T = TestFixture::T;
627627

628628
if (IV::capacity() == 0) {
629-
EXPECT_THROW(IV({T{20}}), std::bad_alloc);
629+
SAFE_EXPECT_THROW(IV({T{20}}), std::bad_alloc);
630630
return;
631631
}
632632

@@ -657,7 +657,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignInitializerList) {
657657

658658
if (IV::capacity() == 0) {
659659
IV device;
660-
EXPECT_THROW(device = {T{52}}, std::bad_alloc);
660+
SAFE_EXPECT_THROW(device = {T{52}}, std::bad_alloc);
661661
return;
662662
}
663663

@@ -787,7 +787,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignIterRange) {
787787
EXPECT_EQ(device, correct);
788788

789789
std::array<T, IV::capacity() + 1> ref{};
790-
EXPECT_THROW(device.assign(ref.begin(), ref.end()), std::bad_alloc);
790+
SAFE_EXPECT_THROW(device.assign(ref.begin(), ref.end()), std::bad_alloc);
791791
}
792792

793793
{
@@ -802,7 +802,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignIterRange) {
802802
// [containers.sequences.inplace.vector.overview]
803803
// 5. Any member function of inplace_vector<T, N> that would cause the size
804804
// to exceed N throws an exception of type bad_alloc.
805-
EXPECT_THROW(
805+
SAFE_EXPECT_THROW(
806806
device.assign(InputIterator{0}, InputIterator{IV::max_size() + 1}),
807807
std::bad_alloc);
808808
}
@@ -833,7 +833,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignRange) {
833833
std::array<T, IV::capacity() + 1> ref;
834834
std::copy(correct.begin(), correct.end(), ref.begin());
835835
ref.back() = T{5};
836-
EXPECT_THROW(device.assign_range(ref), std::bad_alloc);
836+
SAFE_EXPECT_THROW(device.assign_range(ref), std::bad_alloc);
837837
}
838838

839839
// a.assign(il)
@@ -846,7 +846,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignFuncInitializerList) {
846846
auto device = this->unique();
847847

848848
if (device.capacity() == 0) {
849-
EXPECT_THROW(device.assign({T{50}}), std::bad_alloc);
849+
SAFE_EXPECT_THROW(device.assign({T{50}}), std::bad_alloc);
850850
return;
851851
}
852852

@@ -916,7 +916,8 @@ TYPED_TEST(SequenceContainerRequirements, AssignMulti) {
916916
}
917917

918918
device.clear();
919-
EXPECT_THROW(device.assign(device.capacity() + 1, T{12}), std::bad_alloc);
919+
SAFE_EXPECT_THROW(device.assign(device.capacity() + 1, T{12}),
920+
std::bad_alloc);
920921
}
921922

922923
// a.front()
@@ -1017,7 +1018,7 @@ TYPED_TEST(SequenceContainerRequirements, ElementAccessAt) {
10171018
EXPECT_EQ(device.at(i), *(device.begin() + i));
10181019
}
10191020

1020-
EXPECT_THROW(device.at(IV::capacity()), std::out_of_range);
1021+
SAFE_EXPECT_THROW(device.at(IV::capacity()), std::out_of_range);
10211022
}
10221023

10231024
}; // namespace

tests/beman/inplace_vector/gtest_setup.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,11 @@ template <typename Param> class IVBasicTest : public ::testing::Test {
392392

393393
template <typename Param>
394394
std::size_t IVBasicTest<Param>::InputIterator::num_deref;
395+
396+
#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
397+
#define SAFE_EXPECT_THROW(x, y) \
398+
do { \
399+
} while (0)
400+
#else
401+
#define SAFE_EXPECT_THROW(x, y) EXPECT_THROW(x, y)
402+
#endif

tests/beman/inplace_vector/inplace_vector.test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ template <typename T> constexpr void test() {
6565
assert(const_data == std::addressof(const_front));
6666
}
6767

68+
#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
69+
int main() {
70+
test<int>();
71+
return 0;
72+
}
73+
#else
6874
void test_exceptions() {
6975
using vec = inplace_vector<int, 42>;
7076
{
@@ -86,8 +92,10 @@ void test_exceptions() {
8692
}
8793
}
8894
}
95+
8996
int main() {
9097
test<int>();
9198
test_exceptions();
9299
return 0;
93100
}
101+
#endif

0 commit comments

Comments
 (0)