├── .clang-format ├── .github ├── FUNDING.yml └── workflows │ ├── clang.yml │ └── gcc.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── include ├── array.hpp ├── exactly.hpp ├── fizzbuzz_compiler_error.hpp ├── madness.hpp ├── maybe.hpp ├── null.hpp ├── shortest_visit.hpp ├── union.hpp ├── unreachable_t.hpp └── voidify.hpp └── tests └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit : 110 3 | AllowShortFunctionsOnASingleLine: false 4 | AllowShortLambdasOnASingleLine: Inline 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortLoopsOnASingleLine: false 7 | UseTab: Never 8 | IndentWidth: 2 9 | NamespaceIndentation: All 10 | PointerAlignment: Left -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [kelbon] 4 | 5 | -------------------------------------------------------------------------------- /.github/workflows/clang.yml: -------------------------------------------------------------------------------- 1 | name: clang 2 | 3 | on: push 4 | 5 | jobs: 6 | 7 | main: 8 | runs-on: ubuntu-22.04 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Install dependencies 14 | run: | 15 | sudo apt-get update 16 | sudo apt-get install ninja-build lld clang-14 17 | sudo ln -sf /usr/local/bin/ld /usr/bin/lld 18 | 19 | - name: Configure CMake 20 | run: | 21 | cmake . -B build \ 22 | -G "Ninja" \ 23 | -DCMAKE_BUILD_TYPE=Debug \ 24 | -DCMAKE_C_COMPILER=clang-14 \ 25 | -DCMAKE_CXX_COMPILER=clang++-14 \ 26 | - name: Build 27 | run: 28 | cmake --build build -------------------------------------------------------------------------------- /.github/workflows/gcc.yml: -------------------------------------------------------------------------------- 1 | name: gcc 2 | 3 | on: push #[push, pull_request] 4 | 5 | jobs: 6 | 7 | main: 8 | runs-on: ubuntu-22.04 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Install dependencies 14 | run: | 15 | sudo apt-get update 16 | sudo apt-get install ninja-build lld gcc-10 g++-10 17 | sudo ln -sf /usr/local/bin/ld /usr/bin/lld 18 | - name: Configure CMake 19 | run: | 20 | cmake . -B build \ 21 | -G "Ninja" \ 22 | -DCMAKE_BUILD_TYPE=Debug \ 23 | -DCMAKE_C_COMPILER=gcc-10 \ 24 | -DCMAKE_CXX_COMPILER=g++-10 \ 25 | 26 | - name: Build 27 | run: 28 | cmake --build build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(UBGE LANGUAGES CXX) 3 | 4 | file(GLOB ${PROJECT_NAME}_SRCES ${CMAKE_CURRENT_SOURCE_DIR}/include/* ) 5 | add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SRCES} ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp) 6 | add_library(${PROJECT_NAME}lib INTERFACE ${${PROJECT_NAME}_SRCES}) 7 | target_include_directories(${PROJECT_NAME}lib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) 8 | target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 9 | set_property(TARGET ${PROJECT_NAME}lib PROPERTY LINKER_LANGUAGE CXX) 10 | set_property(TARGET ${PROJECT_NAME} PROPERTY LINKER_LANGUAGE CXX) 11 | source_group(Include REGULAR_EXPRESSION ${CMAKE_CURRENT_SOURCE_DIR}/include/*) 12 | 13 | set_property(TARGET ${PROJECT_NAME} PROPERTY CMAKE_CXX_STANDARD_REQUIRED ON) 14 | set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Undefined-Behavior-Gold-Edition 2 | Esoteric, beautiful or strange samples of C++ code 3 | 4 | Now there are: 5 | 6 | * shortest possibrle realization of `std::visit` 7 | * `voidify` for functions which converts any other function to signature `void(void*)` on compile time 8 | * `maybe` which replaces the last semantic meaning of pointers in modern C++ 9 | * `unreachable_t` for writing expressions like `return IsX(b) ? foobar(b) : art::unreachable();` 10 | * `array` realization without array(even C array) 11 | * `union` realization without union and with less error prone interface 12 | 13 | More description is in the implementations themselves 14 | -------------------------------------------------------------------------------- /include/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // implements array without C array, which means array in core C++ is useless 7 | namespace art { 8 | 9 | template 10 | struct value { 11 | T v; 12 | }; 13 | 14 | template 15 | struct array_impl; 16 | 17 | template 18 | struct array_impl> : value... { 19 | using value::value...; 20 | constexpr array_impl() = default; 21 | // clang-format off 22 | template 23 | requires(sizeof...(Ts) == sizeof...(Is)) 24 | constexpr array_impl(Ts&&... args) noexcept((std::is_nothrow_constructible_v && ...)) 25 | : value{std::forward(args)}... { 26 | } 27 | template 28 | requires(sizeof...(Ts) < sizeof...(Is) && std::is_default_constructible_v) 29 | constexpr array_impl(std::initializer_list l) 30 | noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_default_constructible_v) 31 | : value{Is < l.size() ? *std::next(l.begin(), Is) : T{}}... { 32 | } 33 | // clang-format on 34 | }; 35 | 36 | template 37 | struct array : array_impl> { 38 | using array::array_impl::array_impl; 39 | T& operator[](size_t I) noexcept { 40 | return reinterpret_cast(voidify(*this))[I]; 41 | } 42 | const T& operator[](size_t I) const noexcept { 43 | return reinterpret_cast(voidifiy(*this))[I]; 44 | } 45 | T* begin() noexcept { 46 | return reinterpret_cast(this); 47 | } 48 | T* end() noexcept { 49 | return reinterpret_cast(this) + N; 50 | } 51 | const T* begin() const noexcept { 52 | return reinterpret_cast(this); 53 | } 54 | const T* end() const noexcept { 55 | return reinterpret_cast(this) + N; 56 | } 57 | }; 58 | 59 | } // namespace art -------------------------------------------------------------------------------- /include/exactly.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // helper to check if type has EXACTLY required overload without conversions 6 | // (but may be problem if resticted template overload wins in resolution..) 7 | namespace art { 8 | template 9 | struct exactly { 10 | template > U> 11 | operator U&&(); 12 | }; 13 | } // namespace art 14 | -------------------------------------------------------------------------------- /include/fizzbuzz_compiler_error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef CALL_FIZZ_BUZZ 3 | 4 | #include 5 | 6 | // returns result of fizz buzz in compiler error 7 | namespace art { 8 | 9 | template 10 | struct c {}; 11 | template 12 | struct list {}; 13 | 14 | enum : unsigned { fizz = 'fizz', buzz = 'buzz', fizzbuzz = fizz + buzz }; 15 | 16 | template 17 | consteval auto to_string(c) { 18 | if constexpr (!(Value % 15)) 19 | return c<'f', 'i', 'z', 'z', 'b', 'u', 'z', 'z'>{}; 20 | else if constexpr (!(Value % 5)) 21 | return c<'f', 'i', 'z', 'z'>{}; 22 | else if constexpr (!(Value % 3)) 23 | return c<'b', 'u', 'z', 'z'>{}; 24 | else if constexpr (Value < 10) 25 | return c{}; 26 | else 27 | return to_string(c{}); 28 | } 29 | 30 | int fizz_buzz() { 31 | return [](std::index_sequence) { 32 | return list(c<>{}))...>{}; 33 | } 34 | (std::make_index_sequence<100>{}); 35 | } 36 | 37 | } // namespace art 38 | 39 | #endif -------------------------------------------------------------------------------- /include/madness.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Its pure madness, C++ have deducing by return type(really, specialization without saying for what specialization is...) 4 | // Please, don't code like that! 5 | namespace madness { 6 | 7 | class cell { 8 | public: 9 | template 10 | T value() const; 11 | }; 12 | 13 | template <> 14 | float cell::value() const { 15 | return 3.14f; 16 | } 17 | 18 | template <> 19 | double cell::value() const { 20 | return 3.14; 21 | } 22 | 23 | } // namespace madness -------------------------------------------------------------------------------- /include/maybe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // In modern C++ there views with different semantic: 7 | // T& 8 | // - non-owning 9 | // - exactly one valid T 10 | // T* 11 | // - non-owning 12 | // - 0 or 1 valid T 13 | // std::unique/shared_ptr 14 | // - OWNING 15 | // - 0 or 1 valid T 16 | // std::span 17 | // - non owning 18 | // - contiguous in memory some count of T's 19 | // pair iterator + sentinel(views) 20 | // - non owning 21 | // - some count of T's 22 | // containers(ranges) 23 | // - OWNING 24 | // - things with some count of T 25 | // 26 | // But many people still make mistakes such as: 27 | // - owner raw pointer 28 | // - pointer in signature when you dont expect nullptr 29 | // - pointer to more then one value like void foo(T*, size_t) (it must be void foo(std::span) ) 30 | // 31 | // So this header implements replacement for last semantic of T* in C++ 32 | // maybe cant be deleted(-1 error) 33 | // no pointer ariphmetic or operator[], so <= 1 value always semantically 34 | // it is obvious in signature of function, that maybe may be null 35 | namespace art { 36 | 37 | // non-owning nullable view to 1(or 0) valid T 38 | template 39 | struct maybe { 40 | private: 41 | T* value_ptr = nullptr; // invalid for references, and it is correct maybe is semanticly bad 42 | public: 43 | maybe() = default; 44 | constexpr maybe(T* p) noexcept : value_ptr(p) { 45 | } 46 | constexpr maybe(std::nullptr_t) noexcept : maybe() { 47 | } 48 | constexpr bool operator==(std::nullptr_t) const noexcept { 49 | return value_ptr == nullptr; 50 | } 51 | constexpr maybe& operator=(T* p) noexcept { 52 | value_ptr = p; 53 | return *this; 54 | } 55 | constexpr maybe& operator=(std::nullptr_t) noexcept { 56 | value_ptr = nullptr; 57 | return *this; 58 | } 59 | constexpr T* operator->() noexcept { 60 | assert(value_ptr != nullptr); 61 | return value_ptr; 62 | } 63 | constexpr const T* operator->() const noexcept { 64 | assert(value_ptr != nullptr); 65 | return value_ptr; 66 | } 67 | constexpr T& operator*() noexcept { 68 | assert(value_ptr != nullptr); 69 | return *value_ptr; 70 | } 71 | constexpr const T& operator*() const noexcept { 72 | assert(value_ptr != nullptr); 73 | return *value_ptr; 74 | } 75 | }; 76 | 77 | } // namespace art -------------------------------------------------------------------------------- /include/null.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // unifies concept of NULL, things such as nullptr, nullopt, npos(nullsize_t?) 7 | // nullany??(anyany if you knows...), possibly nullpoint/nullgeomobj in geometry 8 | // O(-inf; -inf) for example 9 | namespace art { 10 | 11 | template 12 | concept one_of = (std::same_as || ...); 13 | 14 | struct null_t { 15 | // breakes compilation for every other type(such as void*, if it was operators nullptr_t/nullopt_t) 16 | template T> 17 | consteval operator T() const noexcept { 18 | constexpr struct {} any_empty; 19 | return std::bit_cast(any_empty); 20 | } 21 | }; 22 | 23 | constexpr inline null_t null = {}; 24 | 25 | } // namespace art -------------------------------------------------------------------------------- /include/shortest_visit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define fwd(b) ::std::forward(b) 9 | 10 | // shortest possible realization of std::visit (+-) 11 | namespace art { 12 | // okay it must be const / volatile/ const volatile versions, 13 | // but im lazy(it must be used with STTL::instance_of / types::extract + expand 14 | template 15 | constexpr decltype(auto) visit(auto&& f, std::variant& var) { 16 | return []() consteval { 17 | return std::array{+[](void* ptr, decltype(f)&& f_) -> decltype(auto) { 18 | return fwd(f_)(*std::get_if(reinterpret_cast*>(ptr))); 19 | }...}; 20 | } 21 | ()[var.index()](voidify(var), fwd(f)); 22 | } 23 | constexpr decltype(auto) visit(auto&& f, auto&& var, auto&&... vars) { 24 | return art::visit([&](auto&& v) { return art::visit(std::bind_front(fwd(f), fwd(v)), fwd(vars)...); }, 25 | fwd(var)); 26 | } 27 | 28 | } // namespace art -------------------------------------------------------------------------------- /include/union.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // implements union without union and with better interface. 10 | // This means, that C++ must deprecate fcn union and remove a huge amount of garbage in the standard associated with it 11 | namespace art { 12 | 13 | template 14 | concept type = !std::is_union_v>; 15 | 16 | template 17 | concept any_of = (std::same_as || ...); 18 | 19 | template 20 | struct ctor { 21 | // TODO consteval(internal compiler error now, 18-09-2022) 22 | constexpr ctor() = default; 23 | constexpr ctor(T&& value) noexcept(std::is_nothrow_move_constructible_v) { 24 | auto* p = static_cast(this); 25 | std::construct_at(reinterpret_cast(p), std::move(value)); 26 | } 27 | constexpr ctor(const T& value) noexcept(std::is_nothrow_copy_constructible_v) { 28 | auto* p = static_cast(this); 29 | std::construct_at(reinterpret_cast(p), value); 30 | on_debug(p->index = ) 31 | } 32 | }; 33 | template 34 | struct first : std::type_identity {}; 35 | #ifndef _NDEBUG 36 | #define on_debug(...) __VA_ARGS__ 37 | #else 38 | #define on_debug(...) 39 | #endif 40 | template 41 | struct union_t : private ctor, Ts>... { 42 | private: 43 | // clang-bug now here(with ranges) 44 | alignas(std::ranges::max({alignof(Ts)...})) std::array buf; 45 | on_debug(std::size_t index;) 46 | using first_t = typename first::type; 47 | 48 | public: 49 | constexpr union_t() noexcept(std::is_nothrow_default_constructible_v) requires( 50 | std::is_default_constructible_v) 51 | on_debug( 52 | : index(0)) { 53 | std::construct_at(reinterpret_cast(buf.data())); 54 | } 55 | using ctor, Ts>::ctor...; 56 | template U, typename... Args> 57 | [[nodiscard]] U& emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v) { 58 | return *std::construct_at(reinterpret_cast(&buf), std::forward(args)...); 59 | } 60 | template U> 61 | U& read_as() noexcept { 62 | return *reinterpret_cast>(&buf); 63 | } 64 | template U> 65 | const U& read_as() const noexcept { 66 | return *reinterpret_cast>(&buf); 67 | } 68 | template U> 69 | void destroy_as() const noexcept { 70 | std::destroy_at(reinterpret_cast(reinterpret_cast(&buf))); 71 | } 72 | }; 73 | 74 | } // namespace art -------------------------------------------------------------------------------- /include/unreachable_t.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace art { 6 | 7 | struct [[maybe_unused]] unreachable_t final { 8 | // cant create it 9 | unreachable_t() = delete; 10 | // do it non trivial copy constructible(bit cast forbitten) 11 | unreachable_t(const unreachable_t&) noexcept { 12 | } 13 | // never called, must be without returns, but fcn msvc requires it 14 | template 15 | [[noreturn]] constexpr operator T&() const noexcept { 16 | std::terminate(); 17 | } 18 | template 19 | [[noreturn]] constexpr operator T&&() const noexcept { 20 | std::terminate(); 21 | } 22 | }; 23 | 24 | [[noreturn]] unreachable_t unreachable() { 25 | std::terminate(); 26 | // TODO std::unreachable in C++23 27 | } 28 | } // namespace art -------------------------------------------------------------------------------- /include/voidify.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // erases any function to signature void(void*) on compilation, 8 | // This means C++ standard's ban cast function pointer to void* on constexpr is meaningless 9 | namespace art { 10 | 11 | // for variable 12 | void* voidify(auto&& value) noexcept { 13 | return const_cast(reinterpret_cast(std::addressof(value))); 14 | } 15 | // for function 16 | template 17 | consteval auto voidify_fn(R (*)(Args...)) noexcept { 18 | return +[](void* ptr) -> void { 19 | auto* p = reinterpret_cast*>(ptr); 20 | auto it = p->begin(); 21 | auto* ret = reinterpret_cast< 22 | std::conditional_t, std::add_pointer_t*, std::add_pointer_t>>(*it); 23 | std::advance(it, 1); 24 | [&](std::index_sequence) { // need for ordering in function call 25 | std::construct_at( 26 | ret, 27 | Foo(static_cast(*reinterpret_cast>(*std::next(it, Is)))...)); 28 | } // INVOKED HERE 29 | (std::index_sequence_for{}); 30 | }; 31 | } 32 | // for function which returns void 33 | template 34 | consteval auto voidify_fn(void (*)(Args...)) noexcept { 35 | return +[](void* ptr) -> void { 36 | auto* p = reinterpret_cast*>(ptr); 37 | auto it = p->begin(); 38 | [&](std::index_sequence) { // need for ordering in function call 39 | Foo(static_cast(*reinterpret_cast>(*std::next(it, Is)))...); 40 | } // INVOKED HERE 41 | (std::index_sequence_for{}); 42 | }; 43 | } 44 | 45 | // general case 46 | template 47 | R call_voidified_as(R (*)(Args...), void (*erased_foo)(void*), std::type_identity_t... args) { 48 | using Ret = std::conditional_t, std::add_pointer_t, R>; 49 | alignas(Ret) std::byte buf[sizeof(Ret)]; 50 | std::initializer_list list{voidify(buf), voidify(args)...}; 51 | erased_foo(&list); 52 | return std::move(*reinterpret_cast(buf)); 53 | } 54 | // returns void 55 | template 56 | void call_voidified_as(void (*)(Args...), void (*erased_foo)(void*), std::type_identity_t... args) { 57 | std::initializer_list list{voidify(args)...}; 58 | erased_foo(&list); 59 | } 60 | 61 | } // namespace art 62 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | void foo(const std::variant a, std::variant b) { 15 | std::visit([](auto&&... args) { (std::cout << ... << args); }, a, b); 16 | } 17 | [[noreturn]] art::unreachable_t my_exit(int i) noexcept { 18 | std::exit(i); 19 | } 20 | bool condition() { 21 | return true; 22 | } 23 | bool do_smth() { 24 | return false; 25 | } 26 | template 27 | [[noreturn]] art::unreachable_t throw_(Ts&&... args) { 28 | throw Exception{std::forward(args)...}; 29 | } 30 | int foobar(double i, float j, int a) { 31 | std::cout << i << '\t' << j << '\t' << a << std::endl; 32 | return i; 33 | } 34 | void foobarvoid(std::string s) { 35 | std::cout << s << '\n'; 36 | } 37 | struct check1 { 38 | static void foo(int, double); 39 | static void foo(int, float); 40 | static void foo(auto, double); 41 | }; 42 | 43 | struct check2 { 44 | static void foo(int, float); 45 | static void foo(int, int); 46 | }; 47 | template 48 | concept has = requires(art::exactly v0, art::exactly v1) { 49 | T::foo(v0, v1); 50 | }; 51 | int main() { 52 | std::variant d{5.}; 53 | std::variant dd{5}; 54 | art::visit([](auto&&... x) { (std::cout << ... << std::is_const_v>); }, d, dd); 55 | static_assert(has); 56 | static_assert(!has); 57 | #ifdef CALL_FIZZ_BUZZ 58 | art::fizz_buzz(); 59 | #endif 60 | true ? foobarvoid("") : std::terminate(); 61 | int i = 0; 62 | art::maybe mb = &i; 63 | art::maybe mb1 = &i; 64 | std::cout << *mb; 65 | std::cout << *mb1; 66 | std::string s = "hello world"; 67 | art::maybe mbs = &s; 68 | art::maybe mbs1 = &std::as_const(s); 69 | mbs->resize(1); 70 | std::cout << mbs1->size() << '\n'; 71 | constexpr auto erased_foo = art::voidify_fn<&foobar>(&foobar); 72 | auto res = art::call_voidified_as(&foobar, erased_foo, 143.5, 3.14f, 15); 73 | std::cout << res << '\n'; 74 | constexpr auto erased_foo2 = art::voidify_fn<&foobarvoid>(&foobarvoid); 75 | art::union_t u; 76 | (void)u.emplace("union is useless"); 77 | auto& x = u.read_as(); 78 | (void)x; 79 | u.destroy_as(); 80 | art::array arr{1, 2, 34}; 81 | for (auto i : arr) { 82 | std::cout << i << '\n'; 83 | } 84 | art::call_voidified_as(&foobarvoid, erased_foo2, "hello world"); 85 | foo(5, 'c'); 86 | condition() ? do_smth() : my_exit(-1); 87 | condition() ? do_smth() : throw_(); 88 | } --------------------------------------------------------------------------------