Repository: bemanproject/inplace_vector
Pull request #88: noexceptions
Author: login: 20162026
Created: 2025-05-11T21:42:52Z | Merged: 2025-05-20T14:49:39Z

add support for builds with exceptions disabled #72 


Diff:
diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
index 7c74165..8337f8f 100644
--- a/.github/workflows/ci_tests.yml
+++ b/.github/workflows/ci_tests.yml
@@ -55,6 +55,8 @@ jobs:
             args: "-DCMAKE_CXX_FLAGS=-fsanitize=thread"
           - description: "ASan"
             args: "-DCMAKE_CXX_FLAGS='-fsanitize=address -fsanitize=undefined'"
+          - description: "NoExcep"
+            args: "-DBEMAN_INPLACE_VECTOR_NO_EXCEPTIONS=on"
         include:
           - platform: ubuntu-24.04
             compiler:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c6a78f..e5330c7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,18 @@ option(
     ${PROJECT_IS_TOP_LEVEL}
 )
 
+option(
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
+    "Disable exceptions by replacing throw calls with abort. Default: OFF. Values: { ON, OFF }."
+    OFF
+)
+
+configure_file(
+    "${PROJECT_SOURCE_DIR}/include/beman/inplace_vector/config.hpp.in"
+    "${PROJECT_BINARY_DIR}/include/beman/inplace_vector/config.hpp"
+    @ONLY
+)
+
 include(FetchContent)
 include(GNUInstallDirs)
 
@@ -38,6 +50,7 @@ target_include_directories(
     beman.inplace_vector
     INTERFACE
         $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
         $<INSTALL_INTERFACE:include>
 )
 
diff --git a/include/beman/inplace_vector/config.hpp.in b/include/beman/inplace_vector/config.hpp.in
new file mode 100644
index 0000000..b551164
--- /dev/null
+++ b/include/beman/inplace_vector/config.hpp.in
@@ -0,0 +1,6 @@
+#ifndef BEMAN_INPLACE_VECTOR_CONFIG_HPP
+#define BEMAN_INPLACE_VECTOR_CONFIG_HPP
+
+#cmakedefine01 BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+
+#endif
diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp
index aa6a7fa..da3c45d 100644
--- a/include/beman/inplace_vector/inplace_vector.hpp
+++ b/include/beman/inplace_vector/inplace_vector.hpp
@@ -3,6 +3,10 @@
 #ifndef BEMAN_INPLACE_VECTOR_INPLACE_VECTOR_HPP
 #define BEMAN_INPLACE_VECTOR_INPLACE_VECTOR_HPP
 
+#if !defined(__has_include) || __has_include(<beman/inplace_vector/config.hpp>)
+#include <beman/inplace_vector/config.hpp>
+#endif
+
 #include <algorithm> // for rotate...
 #include <array>
 #include <compare>
@@ -10,19 +14,27 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#ifndef BEMAN_IV_THROW_OR_ABORT
+#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+#include <cstdlib> // for abort
+#define BEMAN_IV_THROW_OR_ABORT(x) abort()
+#else
+#include <stdexcept> // for length_error
+#define BEMAN_IV_THROW_OR_ABORT(x) throw x
+#endif
+#endif
+
 // beman::from_range_t
 namespace beman {
 struct from_range_t {};
@@ -260,8 +272,9 @@ struct inplace_vector
   static constexpr size_type max_size() noexcept { return N; }
   static constexpr size_type capacity() noexcept { return N; }
   constexpr void reserve(size_type n) {
-    if (n > N) [[unlikely]]
-      throw std::bad_alloc();
+    if (n > N) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+    }
   }
   constexpr void shrink_to_fit() {}
 
@@ -351,8 +364,9 @@ struct inplace_vector
   constexpr T &emplace_back(Args &&...args)
     requires(std::constructible_from<T, Args...>)
   {
-    if (!try_emplace_back(std::forward<Args>(args)...)) [[unlikely]]
-      throw std::bad_alloc();
+    if (!try_emplace_back(std::forward<Args>(args)...)) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+    }
     return back();
   }
   constexpr T &push_back(const T &x)
@@ -395,12 +409,14 @@ struct inplace_vector
     requires(std::constructible_from<T, std::ranges::range_reference_t<R>>)
   {
     if constexpr (std::ranges::sized_range<R>) {
-      if (size() + std::ranges::size(rg) > capacity()) [[unlikely]]
-        throw std::bad_alloc();
+      if (size() + std::ranges::size(rg) > capacity()) [[unlikely]] {
+        BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+      }
     }
     for (auto &&e : rg) {
-      if (size() == capacity()) [[unlikely]]
-        throw std::bad_alloc();
+      if (size() == capacity()) [[unlikely]] {
+        BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+      }
       emplace_back(std::forward<decltype(e)>(e));
     }
   }
@@ -438,8 +454,9 @@ struct inplace_vector
     assert_iterator_in_range(position);
     if constexpr (std::random_access_iterator<InputIterator>) {
       if (size() + static_cast<size_type>(std::distance(first, last)) >
-          capacity()) [[unlikely]]
-        throw std::bad_alloc{};
+          capacity()) [[unlikely]] {
+        BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+      }
     }
     auto b = end();
     for (; first != last; ++first)
@@ -555,9 +572,9 @@ struct inplace_vector
   {
     if (sz == size())
       return;
-    else if (sz > N) [[unlikely]]
-      throw std::bad_alloc{};
-    else if (sz > size())
+    else if (sz > N) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+    } else if (sz > size())
       insert(end(), sz - size(), c);
     else {
       unsafe_destroy(begin() + sz, end());
@@ -569,25 +586,27 @@ struct inplace_vector
   {
     if (sz == size())
       return;
-    else if (sz > N) [[unlikely]]
-      throw std::bad_alloc{};
-    else if (sz > size())
+    else if (sz > N) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::bad_alloc());
+    } else if (sz > size()) {
       while (size() != sz)
         emplace_back(T{});
-    else {
+    } else {
       unsafe_destroy(begin() + sz, end());
       unsafe_set_size(sz);
     }
   }
 
   constexpr reference at(size_type pos) {
-    if (pos >= size()) [[unlikely]]
-      throw std::out_of_range("inplace_vector::at");
+    if (pos >= size()) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::out_of_range("inplace_vector::at"));
+    }
     return details::inplace_vector::index(*this, pos);
   }
   constexpr const_reference at(size_type pos) const {
-    if (pos >= size()) [[unlikely]]
-      throw std::out_of_range("inplace_vector::at");
+    if (pos >= size()) [[unlikely]] {
+      BEMAN_IV_THROW_OR_ABORT(std::out_of_range("inplace_vector::at"));
+    }
     return details::inplace_vector::index(*this, pos);
   }
 
@@ -606,7 +625,7 @@ struct inplace_vector
              std::copyable<T>)
   {
     for (auto &&e : x)
-      emplace_back(e);
+      unchecked_emplace_back(e);
   }
 
   constexpr inplace_vector(inplace_vector &&x)
@@ -618,7 +637,7 @@ struct inplace_vector
              std::movable<T>)
   {
     for (auto &&e : x)
-      emplace_back(std::move(e));
+      unchecked_emplace_back(std::move(e));
   }
 
   constexpr inplace_vector &operator=(const inplace_vector &x)
@@ -636,7 +655,7 @@ struct inplace_vector
   {
     clear();
     for (auto &&e : x)
-      emplace_back(e);
+      unchecked_emplace_back(e);
     return *this;
   }
 
@@ -655,7 +674,7 @@ struct inplace_vector
   {
     clear();
     for (auto &&e : x)
-      emplace_back(std::move(e));
+      unchecked_emplace_back(std::move(e));
     return *this;
   }
 
diff --git a/tests/beman/inplace_vector/CMakeLists.txt b/tests/beman/inplace_vector/CMakeLists.txt
index 1d80ab2..2f93633 100644
--- a/tests/beman/inplace_vector/CMakeLists.txt
+++ b/tests/beman/inplace_vector/CMakeLists.txt
@@ -43,6 +43,21 @@ add_gtest(size_n_data)
 add_gtest(erasure)
 add_gtest(modifiers)
 
+# only add noexception tests if NO_EXCEPTIONS option is set and compiler supports -fno-exceptions
+if(
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
+    AND (
+        CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
+        OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+    )
+)
+    add_gtest(noexceptions)
+    target_compile_options(
+        beman.inplace_vector.tests.noexceptions
+        PRIVATE -fno-exceptions
+    )
+endif()
+
 # constexpr test
 add_executable(beman.inplace_vector.tests.constexpr constexpr.test.cpp)
 target_link_libraries(
diff --git a/tests/beman/inplace_vector/constructors.test.cpp b/tests/beman/inplace_vector/constructors.test.cpp
index 6e018f8..556f678 100644
--- a/tests/beman/inplace_vector/constructors.test.cpp
+++ b/tests/beman/inplace_vector/constructors.test.cpp
@@ -21,7 +21,7 @@ TYPED_TEST(Constructors, SizedDefault) {
 
   EXPECT_EQ(IV(0), IV{});
 
-  EXPECT_THROW(IV(IV::capacity() + 1), std::bad_alloc);
+  SAFE_EXPECT_THROW(IV(IV::capacity() + 1), std::bad_alloc);
 
   constexpr auto mid_size = std::midpoint(0ul, IV::capacity());
   IV mid(mid_size);
@@ -57,7 +57,7 @@ TYPED_TEST(Constructors, SizedValue) {
     IV device(0, value);
     EXPECT_EQ(device, IV{});
 
-    EXPECT_THROW(IV(IV::capacity() + 1, value), std::bad_alloc);
+    SAFE_EXPECT_THROW(IV(IV::capacity() + 1, value), std::bad_alloc);
   }
 
   if constexpr (IV::capacity() < 1u)
diff --git a/tests/beman/inplace_vector/container_requirements.test.cpp b/tests/beman/inplace_vector/container_requirements.test.cpp
index ec91a00..aaa9c1b 100644
--- a/tests/beman/inplace_vector/container_requirements.test.cpp
+++ b/tests/beman/inplace_vector/container_requirements.test.cpp
@@ -626,7 +626,7 @@ TYPED_TEST(SequenceContainerRequirements, ConstructorInitializerList) {
   using T = TestFixture::T;
 
   if (IV::capacity() == 0) {
-    EXPECT_THROW(IV({T{20}}), std::bad_alloc);
+    SAFE_EXPECT_THROW(IV({T{20}}), std::bad_alloc);
     return;
   }
 
@@ -657,7 +657,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignInitializerList) {
 
   if (IV::capacity() == 0) {
     IV device;
-    EXPECT_THROW(device = {T{52}}, std::bad_alloc);
+    SAFE_EXPECT_THROW(device = {T{52}}, std::bad_alloc);
     return;
   }
 
@@ -787,7 +787,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignIterRange) {
     EXPECT_EQ(device, correct);
 
     std::array<T, IV::capacity() + 1> ref{};
-    EXPECT_THROW(device.assign(ref.begin(), ref.end()), std::bad_alloc);
+    SAFE_EXPECT_THROW(device.assign(ref.begin(), ref.end()), std::bad_alloc);
   }
 
   {
@@ -802,7 +802,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignIterRange) {
     // [containers.sequences.inplace.vector.overview]
     // 5. Any member function of inplace_vector<T, N> that would cause the size
     // to exceed N throws an exception of type bad_alloc.
-    EXPECT_THROW(
+    SAFE_EXPECT_THROW(
         device.assign(InputIterator{0}, InputIterator{IV::max_size() + 1}),
         std::bad_alloc);
   }
@@ -833,7 +833,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignRange) {
   std::array<T, IV::capacity() + 1> ref;
   std::copy(correct.begin(), correct.end(), ref.begin());
   ref.back() = T{5};
-  EXPECT_THROW(device.assign_range(ref), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.assign_range(ref), std::bad_alloc);
 }
 
 // a.assign(il)
@@ -846,7 +846,7 @@ TYPED_TEST(SequenceContainerRequirements, AssignFuncInitializerList) {
   auto device = this->unique();
 
   if (device.capacity() == 0) {
-    EXPECT_THROW(device.assign({T{50}}), std::bad_alloc);
+    SAFE_EXPECT_THROW(device.assign({T{50}}), std::bad_alloc);
     return;
   }
 
@@ -916,7 +916,8 @@ TYPED_TEST(SequenceContainerRequirements, AssignMulti) {
   }
 
   device.clear();
-  EXPECT_THROW(device.assign(device.capacity() + 1, T{12}), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.assign(device.capacity() + 1, T{12}),
+                    std::bad_alloc);
 }
 
 // a.front()
@@ -1017,7 +1018,7 @@ TYPED_TEST(SequenceContainerRequirements, ElementAccessAt) {
     EXPECT_EQ(device.at(i), *(device.begin() + i));
   }
 
-  EXPECT_THROW(device.at(IV::capacity()), std::out_of_range);
+  SAFE_EXPECT_THROW(device.at(IV::capacity()), std::out_of_range);
 }
 
 }; // namespace
diff --git a/tests/beman/inplace_vector/gtest_setup.hpp b/tests/beman/inplace_vector/gtest_setup.hpp
index 4c8a868..1517492 100644
--- a/tests/beman/inplace_vector/gtest_setup.hpp
+++ b/tests/beman/inplace_vector/gtest_setup.hpp
@@ -392,3 +392,11 @@ template <typename Param> class IVBasicTest : public ::testing::Test {
 
 template <typename Param>
 std::size_t IVBasicTest<Param>::InputIterator::num_deref;
+
+#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+#define SAFE_EXPECT_THROW(x, y)                                                \
+  do {                                                                         \
+  } while (0)
+#else
+#define SAFE_EXPECT_THROW(x, y) EXPECT_THROW(x, y)
+#endif
diff --git a/tests/beman/inplace_vector/inplace_vector.test.cpp b/tests/beman/inplace_vector/inplace_vector.test.cpp
index fee9c6b..cfb1d9c 100644
--- a/tests/beman/inplace_vector/inplace_vector.test.cpp
+++ b/tests/beman/inplace_vector/inplace_vector.test.cpp
@@ -65,6 +65,12 @@ template <typename T> constexpr void test() {
   assert(const_data == std::addressof(const_front));
 }
 
+#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+int main() {
+  test<int>();
+  return 0;
+}
+#else
 void test_exceptions() {
   using vec = inplace_vector<int, 42>;
   {
@@ -86,8 +92,10 @@ void test_exceptions() {
     }
   }
 }
+
 int main() {
   test<int>();
   test_exceptions();
   return 0;
 }
+#endif
diff --git a/tests/beman/inplace_vector/modifiers.test.cpp b/tests/beman/inplace_vector/modifiers.test.cpp
index bee74d1..068d2c5 100644
--- a/tests/beman/inplace_vector/modifiers.test.cpp
+++ b/tests/beman/inplace_vector/modifiers.test.cpp
@@ -42,10 +42,10 @@ TYPED_TEST(Modifiers, InsertSingleConstRef) {
   }
 
   T val{272};
-  EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc);
   EXPECT_EQ(device, reference);
 
-  EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc);
   EXPECT_EQ(device, reference);
 }
 
@@ -82,10 +82,10 @@ TYPED_TEST(Modifiers, InsertSingleRV) {
     }
   }
 
-  EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc);
   EXPECT_EQ(device, reference);
 
-  EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc);
   EXPECT_EQ(device, reference);
 }
 
@@ -121,10 +121,10 @@ TYPED_TEST(Modifiers, InsertEmplace) {
     }
   }
 
-  EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc);
   EXPECT_EQ(device, reference);
 
-  EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc);
   EXPECT_EQ(device, reference);
 }
 
@@ -161,7 +161,7 @@ TYPED_TEST(Modifiers, InsertMulti) {
   }
 
   EXPECT_NO_THROW(device.insert(device.begin(), 0, {2538}));
-  EXPECT_THROW(device.insert(device.begin(), 1, {2538}), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.insert(device.begin(), 1, {2538}), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, InsertInitList) {
@@ -201,7 +201,7 @@ TYPED_TEST(Modifiers, InsertInitList) {
 
   auto full = this->unique();
   EXPECT_NO_THROW(full.insert(full.begin(), {}));
-  EXPECT_THROW(full.insert(full.begin(), {T{25}}), std::bad_alloc);
+  SAFE_EXPECT_THROW(full.insert(full.begin(), {T{25}}), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, InsertRange) {
@@ -257,8 +257,9 @@ TYPED_TEST(Modifiers, InsertRange) {
   EXPECT_NO_THROW(device.insert_range(device.begin(), std::array<T, 0>{}));
   EXPECT_EQ(device, reference);
 
-  EXPECT_THROW(device.insert_range(device.begin(), std::array<T, 1>{T{25}}),
-               std::bad_alloc);
+  SAFE_EXPECT_THROW(
+      device.insert_range(device.begin(), std::array<T, 1>{T{25}}),
+      std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, InsertItrRange) {
@@ -315,7 +316,7 @@ TYPED_TEST(Modifiers, InsertItrRange) {
   EXPECT_EQ(device, reference);
 
   std::array<T, 1> single_array{T{25}};
-  EXPECT_THROW(
+  SAFE_EXPECT_THROW(
       device.insert(device.begin(), single_array.begin(), single_array.end()),
       std::bad_alloc);
 }
@@ -375,7 +376,7 @@ TYPED_TEST(Modifiers, PushBackConstRef) {
   }
 
   T val{0};
-  EXPECT_THROW(device.push_back(val), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.push_back(val), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, PushBackRV) {
@@ -401,7 +402,7 @@ TYPED_TEST(Modifiers, PushBackRV) {
   }
 
   T val{0};
-  EXPECT_THROW(device.push_back(val), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.push_back(val), std::bad_alloc);
 }
 
 // TODO: Check if there's extra copies
@@ -427,7 +428,7 @@ TYPED_TEST(Modifiers, EmplaceBack) {
     EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1));
   }
 
-  EXPECT_THROW(device.emplace_back(0), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.emplace_back(0), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, TryEmplaceBack) {
@@ -701,7 +702,7 @@ TYPED_TEST(Modifiers, ReserveNonEmpty) {
   device.reserve(device.capacity());
   EXPECT_EQ(device, reference);
 
-  EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, ReserveEmpty) {
@@ -723,7 +724,7 @@ TYPED_TEST(Modifiers, ReserveEmpty) {
   device.reserve(device.capacity());
   EXPECT_EQ(device, IV());
 
-  EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc);
 }
 
 TYPED_TEST(Modifiers, ShrinkToFitNonEmpty) {
diff --git a/tests/beman/inplace_vector/noexceptions.test.cpp b/tests/beman/inplace_vector/noexceptions.test.cpp
new file mode 100644
index 0000000..709d64e
--- /dev/null
+++ b/tests/beman/inplace_vector/noexceptions.test.cpp
@@ -0,0 +1,161 @@
+#include <array>
+
+#include <cstdlib>
+#define BEMAN_IV_THROW_OR_ABORT(x) abort()
+
+#include "gtest_setup.hpp"
+
+namespace {
+
+// clang-format off
+using exceptionTestTypes = ::testing::Types<
+    TestParam<Trivial, 0>, TestParam<Trivial, 42>,
+    TestParam<NonTrivial, 0>, TestParam<NonTrivial, 42>>;
+// clang-format on
+
+template <typename Param> class NoExceptions : public IVBasicTest<Param> {};
+TYPED_TEST_SUITE(NoExceptions, exceptionTestTypes);
+
+TYPED_TEST(NoExceptions, NonThrowing) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+
+  const auto reference = this->unique();
+
+  IV device;
+
+  device.assign(reference.begin(), reference.end());
+  EXPECT_EQ(device, reference);
+
+  EXPECT_EQ(device.try_emplace_back(T{}), nullptr);
+  EXPECT_EQ(device.try_push_back(T{}), nullptr);
+  auto range = std::array<T, 1>{};
+  EXPECT_EQ(device.try_append_range(range), range.begin());
+
+  IV sanitycheck = this->unique();
+  EXPECT_EQ(sanitycheck.size(), IV::capacity());
+}
+
+TYPED_TEST(NoExceptions, emplace_back) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        device.emplace_back(T{});
+      },
+      ".*");
+}
+
+TYPED_TEST(NoExceptions, resize) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        device.resize(IV::capacity() + 1);
+      },
+      ".*");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        device.resize(IV::capacity() + 1, T{});
+      },
+      ".*");
+}
+
+TYPED_TEST(NoExceptions, reserve) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        device.reserve(IV::capacity() + 1);
+      },
+      ".*");
+}
+
+TYPED_TEST(NoExceptions, append_range) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  EXPECT_DEATH(
+      {
+        IV device{};
+        device.append_range(std::array<T, IV::capacity() + 1>{});
+      },
+      ".*");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        device.append_range(std::array<T, 1>{});
+      },
+      ".*");
+
+  // TODO consider adding test for append_range of unsized range
+}
+
+TYPED_TEST(NoExceptions, insert) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  if constexpr (IV::capacity() > 0) {
+    EXPECT_DEATH(
+        {
+          IV device = this->unique();
+          // test macro fails to compile if we use std::array<T, N> without
+          // parenthesis ()
+          auto range = (std::array<T, IV::capacity()>{});
+          device.insert(device.end(), range.begin(), range.end());
+        },
+        ".*");
+  }
+
+  EXPECT_DEATH(
+      {
+        IV device{};
+        // test macro fails to compile if we use std::array<T, N> without
+        // parenthesis ()
+        auto range = (std::array<T, IV::capacity() + 1>{});
+        device.insert(device.end(), range.begin(), range.end());
+      },
+      ".*");
+}
+
+TYPED_TEST(NoExceptions, at) {
+
+  using IV = TestFixture::IV;
+  using T = TestFixture::T;
+  GTEST_FLAG_SET(death_test_style, "fast");
+
+  EXPECT_DEATH(
+      {
+        IV device = this->unique();
+        auto e = device.at(IV::capacity());
+      },
+      ".*");
+
+  EXPECT_DEATH(
+      {
+        IV device;
+        const auto e = device.at(IV::capacity());
+      },
+      ".*");
+}
+} // namespace
diff --git a/tests/beman/inplace_vector/ref_impl.test.cpp b/tests/beman/inplace_vector/ref_impl.test.cpp
index 835508a..83c0564 100644
--- a/tests/beman/inplace_vector/ref_impl.test.cpp
+++ b/tests/beman/inplace_vector/ref_impl.test.cpp
@@ -42,6 +42,11 @@ static constexpr void __assert_failure(char const *__file, int __line,
           : ::__assert_failure(static_cast<const char *>(__FILE__), __LINE__,  \
                                "assertion failed: " #__VA_ARGS__))
 
+#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+#define CHECK_THROWS(EXPR, EXCEPT)                                             \
+  do {                                                                         \
+  } while (0)
+#else
 #define CHECK_THROWS(EXPR, EXCEPT)                                             \
   if (auto e =                                                                 \
           [&] {                                                                \
@@ -58,6 +63,7 @@ static constexpr void __assert_failure(char const *__file, int __line,
     __assert_failure(static_cast<const char *>(__FILE__), __LINE__,            \
                      "expression failed to throw " #EXCEPT ": " #EXPR);        \
   }
+#endif
 
 template struct beman::details::inplace_vector::storage::zero_sized<int>;
 template struct beman::details::inplace_vector::storage::trivial<int, 10>;
diff --git a/tests/beman/inplace_vector/size_n_data.test.cpp b/tests/beman/inplace_vector/size_n_data.test.cpp
index b46414d..1d45fc8 100644
--- a/tests/beman/inplace_vector/size_n_data.test.cpp
+++ b/tests/beman/inplace_vector/size_n_data.test.cpp
@@ -56,7 +56,7 @@ TYPED_TEST(SizeNCapacity, ResizeUp) {
 
   IV device;
 
-  EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc);
   EXPECT_EQ(device, IV{});
 
   if (device.capacity() == 0)
@@ -86,7 +86,7 @@ TYPED_TEST(SizeNCapacity, ResizeUp) {
   }
 
   IV before_resize(device);
-  EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc);
+  SAFE_EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc);
   EXPECT_EQ(device, before_resize);
 }
 


Reviews:
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T20:36:33Z
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T20:40:33Z
user: login: wusatosi
body: See comments
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-16T20:40:50Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T16:26:46Z
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T16:33:09Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T16:33:35Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T19:16:54Z
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T20:29:53Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T20:37:00Z
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T21:30:24Z
user: login: wusatosi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T03:55:13Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-19T06:05:42Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-19T06:52:25Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-19T21:42:26Z
user: login: wusatosi
body: Thank you so much for all the work!

I have one minor question but besides that everything looks great.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-20T04:27:54Z
user: login: 20162026
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T07:03:14Z

Review comments:
diff_hunk: @@ -10,19 +10,27 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L
+#ifndef BEMAN_IV_THROW
+#include <cstdlib> // for abort
+#define BEMAN_IV_THROW(x) abort()
+#endif
+#else
+#include <stdexcept> // for length_error
+#define BEMAN_IV_THROW(x) throw x
+#endif
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: wusatosi
body: Is this actually the desired behavior?

I think the behavior should be:
BEMAN_IV_THROW has a default value of if `__cpp_exceptions` is not defined.

If BEMAN_IV_THROW, call abort, otherwise, throw the error.
created_at: 2025-05-16T20:36:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,68 @@
+#include <array>
+
+bool abort_called = false;
+#define BEMAN_IV_THROW(x) abort_called = true;
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: wusatosi
body: Is [death test](https://github.com/google/googletest/blob/main/docs/advanced.md#death-tests) available to us?
created_at: 2025-05-16T20:40:33Z
author_association: MEMBER
diff_hunk: @@ -10,19 +10,27 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L
+#ifndef BEMAN_IV_THROW
+#include <cstdlib> // for abort
+#define BEMAN_IV_THROW(x) abort()
+#endif
+#else
+#include <stdexcept> // for length_error
+#define BEMAN_IV_THROW(x) throw x
+#endif
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: 20162026
body: the idea behind `#ifndef BEMAN_IV_THROW` was to allow the user to override default abort behaviour, but I guess it will be better to check it at the top:

```suggestion
#ifndef BEMAN_IV_THROW_OR_ABORT
#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L
#include <cstdlib> // for abort
#define BEMAN_IV_THROW_OR_ABORT(x) abort()
#else
#include <stdexcept> // for length_error
#define BEMAN_IV_THROW_OR_ABORT(x) throw x
#endif
#endif
```
created_at: 2025-05-17T16:26:46Z
author_association: COLLABORATOR
diff_hunk: @@ -10,19 +10,27 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L
+#ifndef BEMAN_IV_THROW
+#include <cstdlib> // for abort
+#define BEMAN_IV_THROW(x) abort()
+#endif
+#else
+#include <stdexcept> // for length_error
+#define BEMAN_IV_THROW(x) throw x
+#endif
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: wusatosi
body: Ah I see what you're trying to do now!
created_at: 2025-05-17T16:33:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,68 @@
+#include <array>
+
+bool abort_called = false;
+#define BEMAN_IV_THROW(x) abort_called = true;
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: 20162026
body: Ok, I somehow missed this... I will reimplement tests using death testing.

Although I don't know if there is much sense to run death tests for all throwing functions with every type from `IVAllTypes`
created_at: 2025-05-17T16:33:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,111 @@
+#include <array>
+
+#include "gtest_setup.hpp"
+
+namespace {
+
+template <typename Param> class NoExceptions : public IVBasicTest<Param> {};
+TYPED_TEST_SUITE(NoExceptions, IVAllTypes);
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: 20162026
body: I guess testing all of the `IVAllTypes`  is excessive. it takes >100s for some of the CI jobs to complete 💀
created_at: 2025-05-17T19:16:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,111 @@
+#include <array>
+
+#include "gtest_setup.hpp"
+
+namespace {
+
+template <typename Param> class NoExceptions : public IVBasicTest<Param> {};
+TYPED_TEST_SUITE(NoExceptions, IVAllTypes);
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: wusatosi
body: Oh no, 100s is definitely excessive for this test. Yeah all types trashes compile time.

All types is used because our implementation switches it's underlying storage between trivial and non trivial type, and there's complications between const and non const. I think we can relax for this test honestly. 
created_at: 2025-05-17T20:29:53Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,111 @@
+#include <array>
+
+#include "gtest_setup.hpp"
+
+namespace {
+
+template <typename Param> class NoExceptions : public IVBasicTest<Param> {};
+TYPED_TEST_SUITE(NoExceptions, IVAllTypes);
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: 20162026
body: after switching to only 4 types it is <10s, is that acceptable? or should we cut it down to a single type?
created_at: 2025-05-17T20:37:00Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,111 @@
+#include <array>
+
+#include "gtest_setup.hpp"
+
+namespace {
+
+template <typename Param> class NoExceptions : public IVBasicTest<Param> {};
+TYPED_TEST_SUITE(NoExceptions, IVAllTypes);
path: tests/beman/inplace_vector/noexceptions.test.cpp
user: login: wusatosi
body: This works! Thanks
created_at: 2025-05-17T21:30:23Z
author_association: MEMBER
diff_hunk: @@ -10,19 +11,28 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#ifndef BEMAN_IV_THROW_OR_ABORT
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L ||                \
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: wusatosi
body: > wusatosi do you mean checking it in the config.hpp or in CMake itself using something like check_cxx_source_compiles ? I have pushed an example of how I would imagine it.

Yes I am referring to using `check_cxx_source_compiles` with something like:

```c++
#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L
#error "No exception Support"
#endif

void main() { throw 0; }
```

And if this does not compile, we default `BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS` to ON with a message.

We then update these lines to:
```suggestion
#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
```
created_at: 2025-05-19T03:55:13Z
author_association: MEMBER
diff_hunk: @@ -10,19 +11,28 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#ifndef BEMAN_IV_THROW_OR_ABORT
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L ||                \
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: 20162026
body: imho checking it inside of the cmake is pretty useless, as it brings no real value over just having the CMake option with default OFF (whole lib must have exceptions enabled or disabled)
created_at: 2025-05-19T06:05:42Z
author_association: COLLABORATOR
diff_hunk: @@ -10,19 +11,28 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#ifndef BEMAN_IV_THROW_OR_ABORT
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L ||                \
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: 20162026
body: If we cant have `__cpp_exceptions` inside of the header, I would rather keep only regular Cmake option, while letting user to override `BEMAN_IV_THROW_OR_ABORT()` if needed

```cpp
#ifndef BEMAN_IV_THROW_OR_ABORT
#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
#include <cstdlib> // for abort
#define BEMAN_IV_THROW_OR_ABORT(x) abort()
#else
#include <stdexcept> // for length_error
#define BEMAN_IV_THROW_OR_ABORT(x) throw x
#endif
#endif

```
created_at: 2025-05-19T06:52:25Z
author_association: COLLABORATOR
diff_hunk: @@ -10,19 +11,28 @@
 #include <cstddef>    // for size_t
 #include <cstdint>    // for fixed-width integer types
 #include <cstdio>     // for assertion diagnostics
-#include <cstdlib>    // for abort
 #include <functional> // for less and equal_to
 #include <iterator>   // for reverse_iterator and iterator traits
 #include <limits>     // for numeric_limits
 #include <memory>     // for destroy
 #include <new>        // for operator new
 #include <ranges>
-#include <stdexcept>   // for length_error
 #include <type_traits> // for aligned_storage and all meta-functions
 
 // Artifact from previous implementation, can be used as hints for optimizer
 #define IV_EXPECT(EXPR)
 
+#ifndef BEMAN_IV_THROW_OR_ABORT
+#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L ||                \
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
path: include/beman/inplace_vector/inplace_vector.hpp
user: login: 20162026
body: I have pushed some changes to avoid NOFLAGFORK roadblock and updated tests accordingly.


I did try replacing `EXPECT_THROW` with `EXPECT_DEATH` when the CMake flag is on, but it extremely increases pipeline runtime...

```
baseline with custom BEMAN_IV_THROW_OR_ABORT override
13 jobs in 8m 5s

add noexcept jobs; replace EXPECT_THROW with EXPECT_DEATH
17 jobs in 21m 49s

add noexcept jobs; replace EXPECT_THROW with do{}while(0); only run noexcept tests with corresponding cmake option
17 jobs in 8m 4s
```


as a side note im not sure if there is much sense to run Asan and Tsan in both release and debug modes
created_at: 2025-05-19T21:42:26Z
author_association: COLLABORATOR
diff_hunk: @@ -392,3 +392,11 @@ template <typename Param> class IVBasicTest : public ::testing::Test {
 
 template <typename Param>
 std::size_t IVBasicTest<Param>::InputIterator::num_deref;
+
+#if BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS()
+#define SAFE_EXPECT_THROW(x, y)                                                \
+  do {                                                                         \
+  } while (0)
+#else
path: tests/beman/inplace_vector/gtest_setup.hpp
user: login: wusatosi
body: This is cool! Great job!
created_at: 2025-05-20T04:17:59Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,21 @@ add_gtest(size_n_data)
 add_gtest(erasure)
 add_gtest(modifiers)
 
+# only add noexception tests if NO_EXCEPTIONS option is set and compiler supports -fno-exceptions
+if(
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
+    AND (
+        CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
+        OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+    )
+)
+    add_gtest(noexceptions)
+    target_compile_options(
+        beman.inplace_vector.tests.noexceptions
+        PRIVATE -fno-exceptions
+    )
path: tests/beman/inplace_vector/CMakeLists.txt
user: login: wusatosi
body: Is this still needed?
created_at: 2025-05-20T04:25:28Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,21 @@ add_gtest(size_n_data)
 add_gtest(erasure)
 add_gtest(modifiers)
 
+# only add noexception tests if NO_EXCEPTIONS option is set and compiler supports -fno-exceptions
+if(
+    BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS
+    AND (
+        CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
+        OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+    )
+)
+    add_gtest(noexceptions)
+    target_compile_options(
+        beman.inplace_vector.tests.noexceptions
+        PRIVATE -fno-exceptions
+    )
path: tests/beman/inplace_vector/CMakeLists.txt
user: login: 20162026
body: Yes, it ensures that there are no throws in the compiled code. Without it, EXPECT_DEATH could still pass if an unhandled exception was thrown.


Although, I'm now not sure if we need the compiler check. It allows running the tests on compilers that don't support or have a different syntax for disabling exceptions (e.g. MSVC). But right now, CI only includes Clang and GCC, and maybe it would be better to error out instead of silently skipping tests on other compilers. On the other hand, it doesn't matter much, as the implementation should be compiler agnostic as long as there is abort()
```c++
        CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
        OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
```

created_at: 2025-05-20T07:03:14Z
author_association: COLLABORATOR

PR comments:
user: login: 20162026
created_at: 2025-05-11T21:48:16Z
body: - We can add the `BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS` CMake option and generate `config.hpp`, but I would like to keep `defined(__cpp_exceptions)` as it's the industry-standard way to handle noexception build.

- ~~current test implementation is ugly, but I dont know how to improve it while keeping gtest~~
author_association: COLLABORATOR
user: login: wusatosi
created_at: 2025-05-13T19:39:28Z
body: Thanks for the contribution as always, I will review tomorrow.
author_association: MEMBER
user: login: wusatosi
created_at: 2025-05-16T20:42:40Z
body: > We can add the BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS CMake option and generate config.hpp, but I would like to keep defined(__cpp_exceptions) as it's the industry-standard way to handle noexception build.

This does make sense to me, I don't know if we want to go with this, we have a meeting agenda on discussion on the config.hpp approach of doing things vs industry standard direct macro detection, I will bring this up.
author_association: MEMBER
user: login: wusatosi
created_at: 2025-05-17T16:32:43Z
body: Hey @20162026 , we will bring up some feedback against CPP.NO_FLAG_FORKING at the weekly sync the coming Monday, I think you have some valuable experience interacting and developing with this rule, would you mind writing a short feedback here so I can forward it to our meeting agenda?
author_association: MEMBER
user: login: 20162026
created_at: 2025-05-17T17:32:21Z
body: @wusatosi some arguments I could think of right now, mostly regarding this and header only libs in general:

1) I don't think that ODR violation prevention measures should be enforced for header only libraries, as it is up to the user how the compilation units that are using the library should behave.
2) For header only (especially single header) libraries, it is inconvenient to have cmake generated config files, as the end user often just wants to drag and drop the headers or use a different build system, and would be forced to manually generate the config file
3) This is more of a unique case and can most likely be avoided by splitting the freestanding impl into a different namespace. However, I have a project where I would like to use both `HOSTED` and `FREESTANDING-DELETED` versions of `inplace_vector`. If we used a preprocessor define to distinguish between them and enforce it at top level, it would require multiple instances of the same library.
4) From personal observation, it is common practice for all of the standard libraries to `FLAG FORK`

author_association: COLLABORATOR
user: login: wusatosi
created_at: 2025-05-17T17:48:20Z
body: Thanks for the comments, will forward.
author_association: MEMBER
user: login: wusatosi
created_at: 2025-05-17T19:16:14Z
body: oh also, how do we refer to you? : )
author_association: MEMBER
user: login: 20162026
created_at: 2025-05-17T20:30:36Z
body: @wusatosi my name is Edgar if that's what you are asking.

Also, PR should be more or less ready, besides the NO_FLAG_FORKING compliance
author_association: COLLABORATOR
user: login: wusatosi
created_at: 2025-05-17T21:41:22Z
body: > We can add the BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS CMake option and generate config.hpp, but I would like to keep defined(__cpp_exceptions) as it's the industry-standard way to handle noexception build.

Do you think the CMake option that has its default value based on if `__cpp_exceptions` is working would be acceptable?

I can set this up directly on this PR, would you mind that?
author_association: MEMBER
user: login: 20162026
created_at: 2025-05-18T14:19:53Z
body: > Do you think the CMake option that has its default value based on if `__cpp_exceptions` is working would be acceptable?

@wusatosi  do you mean checking it in the `config.hpp` or in CMake itself using something like `check_cxx_source_compiles` ? I have pushed an example of how I would imagine it.

> I can set this up directly on this PR, would you mind that?

Feel free to push any changes you see fit. 
PS. there's no need to ask, I’d just uncheck the `Allow edits by maintainers` flag on GitHub otherwise
author_association: COLLABORATOR
user: login: wusatosi
created_at: 2025-05-19T03:56:25Z
body: > Feel free to push any changes you see fit.
> PS. there's no need to ask, I’d just uncheck the Allow edits by maintainers flag on GitHub otherwise

Noted, I usually find it rude for people to push directly on my branch, so I always ask HAHA.
author_association: MEMBER