diff --git a/.clang-format b/.clang-format index 0cc28aa..7592e0d 100644 --- a/.clang-format +++ b/.clang-format @@ -1,100 +1,3 @@ -# Language: Cpp -# BasedOnStyle: LLVM - -# AccessModifierOffset: -4 -# AlignAfterOpenBracket: AlwaysBreak -# AlignConsecutiveAssignments: false -# AlignConsecutiveDeclarations: false -# AlignEscapedNewlines: Left -# AlignOperands: false -# AlignTrailingComments: true -# AllowAllParametersOfDeclarationOnNextLine: true -# AllowShortBlocksOnASingleLine: false -# AllowShortCaseLabelsOnASingleLine: false -# AllowShortFunctionsOnASingleLine: None -# AllowShortIfStatementsOnASingleLine: false -# AllowShortLoopsOnASingleLine: false -# AlwaysBreakAfterReturnType: None -# AlwaysBreakBeforeMultilineStrings: true -# AlwaysBreakTemplateDeclarations: true -# BinPackArguments: false -# BinPackParameters: false -# BreakBeforeBinaryOperators: All -# BreakBeforeBraces: Attach -# BraceWrapping: -# AfterClass: true -# AfterControlStatement: true -# AfterEnum: true -# AfterFunction: true -# AfterNamespace: true -# AfterStruct: true -# AfterUnion: true -# AfterExternBlock: true -# BeforeCatch: true -# BeforeElse: true -# IndentBraces: false -# BreakBeforeTernaryOperators: true -# BreakConstructorInitializersBeforeComma: false -# ColumnLimit: 100 -# CommentPragmas: "^ IWYU pragma:" -# ConstructorInitializerAllOnOneLineOrOnePerLine: false -# ConstructorInitializerIndentWidth: 4 -# ContinuationIndentWidth: 4 -# Cpp11BracedListStyle: true -# DerivePointerAlignment: false -# ExperimentalAutoDetectBinPacking: false -# FixNamespaceComments: true -# ForEachMacros: -# - foreach -# - Q_FOREACH -# - BOOST_FOREACH -# IncludeBlocks: Preserve -# IncludeCategories: -# - Regex: '^"(llvm|llvm-c|clang|clang-c)/' -# Priority: 2 -# - Regex: '^(<|"(gtest|isl|json)/)' -# Priority: 3 -# - Regex: ".*" -# Priority: 1 -# IndentCaseLabels: true -# IndentWidth: 4 -# IndentWrappedFunctionNames: false -# KeepEmptyLinesAtTheStartOfBlocks: true -# MacroBlockBegin: "" -# MacroBlockEnd: "" -# MaxEmptyLinesToKeep: 1 -# NamespaceIndentation: Inner -# ObjCBlockIndentWidth: 2 -# ObjCSpaceAfterProperty: false -# ObjCSpaceBeforeProtocolList: true -# PenaltyBreakBeforeFirstCallParameter: 19 -# PenaltyBreakComment: 300 -# PenaltyBreakFirstLessLess: 80 -# PenaltyBreakString: 1000 -# PenaltyExcessCharacter: 1000000 -# PenaltyReturnTypeOnItsOwnLine: 10 -# PointerAlignment: Left -# ReflowComments: true -# SortIncludes: false -# SpaceAfterCStyleCast: false -# SpaceAfterTemplateKeyword: true -# SpaceBeforeAssignmentOperators: true -# SpaceBeforeCpp11BracedList: false -# SpaceBeforeCtorInitializerColon: true -# SpaceBeforeInheritanceColon: true -# SpaceBeforeParens: ControlStatements -# SpaceBeforeRangeBasedForLoopColon: true -# SpaceInEmptyParentheses: false -# SpacesBeforeTrailingComments: 1 -# SpacesInAngles: false -# SpacesInContainerLiterals: true -# SpacesInCStyleCastParentheses: false -# SpacesInParentheses: false -# SpacesInSquareBrackets: false -# Standard: Cpp11 -# TabWidth: 4 -# UseTab: Never - AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent # New in v14. For earlier clang-format versions, use AlwaysBreak instead. AlignConsecutiveMacros: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 0340d3f..1b9e29c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ project( LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED On) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-backtrace-limit=0") @@ -33,6 +34,7 @@ if(MSVC) endif() endif() +# AddressSanitizer option(ENABLE_ASAN "Enable AddressSanitizer" OFF) if(ENABLE_ASAN) @@ -41,9 +43,29 @@ if(ENABLE_ASAN) add_link_options(-fsanitize=address) endif() +# Function to enable template compilation time measurement for a specific target +function(efp_enable_compilation_time target_name) + message(STATUS "Enabling template compilation time measurement for target: ${target_name}") + + # Check if the compiler is Clang or GCC and add the respective flags to the target + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Clang-specific flags to report template instantiation times for the target + set_property(TARGET ${target_name} APPEND_STRING PROPERTY COMPILE_FLAGS " -ftime-trace") + message(STATUS "Added Clang-specific flags for template instantiation times to ${target_name}") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # GCC-specific flags to report template instantiation times for the target + set_property(TARGET ${target_name} APPEND_STRING PROPERTY COMPILE_FLAGS " -ftemplate-depth=1024 -ftime-report") + message(STATUS "Added GCC-specific flags for template instantiation times to ${target_name}") + endif() +endfunction() + +# ! temp +# Set verbose makefile output +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "Enable verbose output" FORCE) + add_library(efp INTERFACE) target_include_directories(efp INTERFACE "./include") - +target_compile_features(efp INTERFACE cxx_std_11) # Check if this CMakeLists.txt is the top-most CMake project if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 22727c9..ad05933 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -21,4 +21,4 @@ target_link_libraries(io_demo add_executable(format_demo format_demo.cpp) target_link_libraries(format_demo PRIVATE - efp) + efp) \ No newline at end of file diff --git a/include/efp.hpp b/include/efp.hpp index fddff2d..44393b5 100644 --- a/include/efp.hpp +++ b/include/efp.hpp @@ -13,9 +13,9 @@ #include "./efp/scientific.hpp" #include "./efp/sort.hpp" #include "./efp/c_utility.hpp" - #include "./efp/io.hpp" #include "./efp/string.hpp" #include "./efp/format.hpp" +#include "./efp/concurrency.hpp" #endif \ No newline at end of file diff --git a/include/efp/concurrency.hpp b/include/efp/concurrency.hpp new file mode 100644 index 0000000..a4643e8 --- /dev/null +++ b/include/efp/concurrency.hpp @@ -0,0 +1,258 @@ +#ifndef EFP_CONCURRENCY_HPP_ +#define EFP_CONCURRENCY_HPP_ + +// ! Not for freestanding environments +#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 + + #include + #include + #include + #include + + #include "efp/cpp_core.hpp" + #include "efp/maybe.hpp" + #include "efp/cyclic.hpp" + +namespace efp { + +template +using Atomic = std::atomic; + +template +using Arc = std::shared_ptr; + +template +inline Arc arc(A&& a) { + return std::make_shared(std::forward(a)); +}; + +template +class BlockingQ { +public: + BlockingQ() {} + + BlockingQ(const BlockingQ& other) = delete; + + BlockingQ& operator=(const BlockingQ& other) = delete; + + BlockingQ(BlockingQ&& other) = delete; + + BlockingQ& operator=(BlockingQ&& other) = delete; + + ~BlockingQ() {} + + // Add an element to the queue. + void enqueue(A t) { + std::lock_guard lock(_m); + _q.push_back(t); + _c.notify_one(); + } + + // Get the front element. + // If the queue is empty, block till a element is avaiable. + A dequeue() { + std::unique_lock lock(_m); + while (_q.empty()) { + // release lock as long as the wait and reaquire it afterwards. + _c.wait(lock); + } + return _q.pop_front(); + } + +private: + Vcq _q; + mutable std::mutex _m; + std::condition_variable _c; +}; + +template +class BlockingQ { +public: + BlockingQ() {} + + BlockingQ(const BlockingQ& other) = delete; + + BlockingQ& operator=(const BlockingQ& other) = delete; + + BlockingQ(BlockingQ&& other) = delete; + + BlockingQ& operator=(BlockingQ&& other) = delete; + + ~BlockingQ() {} + + // Add an element to the queue. + void enqueue(A t) { + std::lock_guard lock(_m); + _q.push(t); + _c.notify_one(); + } + + // Get the front element. + // If the queue is empty, block till a element is avaiable. + A dequeue() { + std::unique_lock lock(_m); + while (_q.empty()) { + // release lock as long as the wait and reaquire it afterwards. + _c.wait(lock); + } + A val = _q.front(); + _q.pop(); + return val; + } + +private: + std::queue _q; + mutable std::mutex _m; + std::condition_variable _c; +}; + +template +class NonBlockingQ { +public: + NonBlockingQ() {} + + NonBlockingQ(const NonBlockingQ& other) = delete; + + NonBlockingQ& operator=(const NonBlockingQ& other) = delete; + + NonBlockingQ(NonBlockingQ&& other) = delete; + + NonBlockingQ& operator=(NonBlockingQ&& other) = delete; + + ~NonBlockingQ() {} + + // Add an element to the queue. + void enqueue(A t) { + std::lock_guard lock(_m); + _q.push_back(t); + } + + bool is_empty() { + return _q.empty(); + } + + // Get the front element. + // If the queue is empty, return nothing; + Maybe dequeue() { + std::unique_lock lock(_m); + if (_q.empty()) { + return nothing; + } else { + return _q.pop_front(); + } + } + +private: + Vcq _q; + mutable std::mutex _m; +}; + +template +class NonBlockingQ { +public: + NonBlockingQ() {} + + NonBlockingQ(const NonBlockingQ& other) = delete; + + NonBlockingQ& operator=(const NonBlockingQ& other) = delete; + + NonBlockingQ(NonBlockingQ&& other) = delete; + + NonBlockingQ& operator=(NonBlockingQ&& other) = delete; + + ~NonBlockingQ() {} + + // Add an element to the queue. + void enqueue(A t) { + std::lock_guard lock(_m); + _q.push(t); + } + + bool is_empty() { + return _q.empty(); + } + + // Get the front element. + // If the queue is empty, return nothing; + Maybe dequeue() { + std::unique_lock lock(_m); + if (_q.empty()) { + return nothing; + } else { + A val = _q.front(); + _q.pop(); + return val; + } + } + +private: + std::queue _q; + mutable std::mutex _m; +}; + +// DoubleBuffer +// A thread-safe, allocation-free double buffer implementation. +template +class DoubleBuffer { +public: + explicit DoubleBuffer() + : _read_buffer(new Vcq {}), _write_buffer(new Vcq {}) {} + + DoubleBuffer(const DoubleBuffer& other) = default; + + DoubleBuffer& operator=(const DoubleBuffer& other) = default; + + DoubleBuffer(DoubleBuffer&& other) noexcept = default; + + DoubleBuffer& operator=(DoubleBuffer&& other) noexcept = default; + + ~DoubleBuffer() { + delete _read_buffer; + delete _write_buffer; + } + + void enqueue(const A& a) { + _spinlock.lock(); + _write_buffer->push_back(a); + _spinlock.unlock(); + } + + void swap_buffer() { + _spinlock.lock(); + swap(_write_buffer, _read_buffer); + _spinlock.unlock(); + } + + A dequeue() { + return _read_buffer->pop_front(); + } + + bool empty() { + return _read_buffer->empty(); + } + +private: + class Spinlock { + public: + inline void lock() { + while (_flag.test_and_set(std::memory_order_acquire)) {} + } + + inline void unlock() { + _flag.clear(std::memory_order_release); + } + + private: + std::atomic_flag _flag = {0}; + }; + + Spinlock _spinlock; + Vcq* _write_buffer; + Vcq* _read_buffer; +}; + +// todo Implement Sequence traits for DoubleBuffer +} // namespace efp + +#endif // __STDC_HOSTED__ && __STDC_HOSTED__ == 1 +#endif \ No newline at end of file diff --git a/include/efp/enum.hpp b/include/efp/enum.hpp index da709cc..5e7ff01 100644 --- a/include/efp/enum.hpp +++ b/include/efp/enum.hpp @@ -1,181 +1,296 @@ -#ifndef ENUM_HPP_ -#define ENUM_HPP_ +#ifndef EFP_ENUM_HPP +#define EFP_ENUM_HPP #include "efp/meta.hpp" namespace efp { -// Is WildCard -template -using IsWildCard = IsSame>, Tuple<>>; +namespace detail { -// WildCardWrapper + // Use bit operation and recursion + constexpr uint8_t power_2_ceiling(uint8_t n, uint8_t power = 2) { + return (power >= n) ? power : power_2_ceiling(n, power << 1); + } -template -struct WildCardWrapper { - const F& f; + // clang-format off + #define EFP_STAMP2(n, x) x(n) x(n + 1) + #define EFP_STAMP4(n, x) EFP_STAMP2(n, x) EFP_STAMP2(n + 2, x) + #define EFP_STAMP8(n, x) EFP_STAMP4(n, x) EFP_STAMP4(n + 4, x) + #define EFP_STAMP16(n, x) EFP_STAMP8(n, x) EFP_STAMP8(n + 8, x) + #define EFP_STAMP32(n, x) EFP_STAMP16(n, x) EFP_STAMP16(n + 16, x) + #define EFP_STAMP64(n, x) EFP_STAMP32(n, x) EFP_STAMP32(n + 32, x) + #define EFP_STAMP128(n, x) EFP_STAMP64(n, x) EFP_STAMP64(n + 64, x) + #define EFP_STAMP256(n, x) EFP_STAMP128(n, x) EFP_STAMP128(n + 128, x) - template - auto operator()(const Args&...) const -> decltype(declval()()) { - return f(); - } -}; + // Generic case for pattern matching + // Bound the index to the maximum alternative index to reduce the number of template instantiations + #define EFP_ENUM_CASE(i) case i: return Case::call(efp::forward(args)...); break; -template -struct detail::ReturnImpl> { - using Type = decltype(declval()()); -}; + // clang-format on -// MatchBranchImpl -namespace detail { - template - struct MatchBranchImpl { - using Type = CVRefRemoved; + template class Case, typename... Args> + struct _EnumSwitch {}; + + template class Case, typename... Args> + struct _EnumSwitch<2, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP2(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } }; - template - struct MatchBranchImpl::value, void>> { - using Type = WildCardWrapper>; + template class Case, typename... Args> + struct _EnumSwitch<4, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP4(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } }; -} // namespace detail -// MatchBranch + template class Case, typename... Args> + struct _EnumSwitch<8, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP8(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } + }; -template -using MatchBranch = typename detail::MatchBranchImpl::Type; + template class Case, typename... Args> + struct _EnumSwitch<16, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP16(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } + }; -// Overloaded + template class Case, typename... Args> + struct _EnumSwitch<32, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP32(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } + }; -template -struct Overloaded; + template class Case, typename... Args> + struct _EnumSwitch<64, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP64(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } + }; -template -struct Overloaded: F { - using F::operator(); + template class Case, typename... Args> + struct _EnumSwitch<128, alt_num, Case, Args...> { + static auto call(uint8_t index, Args&&... args) + -> decltype(Case<0>::call(std::forward(args)...)) { + switch (index) { + EFP_STAMP128(0, EFP_ENUM_CASE) + default: + throw std::runtime_error("Invalid alternative index"); + } + } + }; - template - Overloaded(const G& g) : F {MatchBranch {g}} {} -}; + // ! temp + // template class Case, typename... Args> + // struct _EnumSwitch<256, alt_num, Case, Args...> { + // static auto call(uint8_t index, Args&&... args) + // -> decltype(Case<0>::call(std::forward(args)...)) { + // switch (index) { + // EFP_STAMP256(0, EFP_ENUM_CASE) + // default: + // throw std::runtime_error("Invalid alternative index"); + // } + // } + // }; -template -struct Overloaded: F, Overloaded { - using F::operator(); - using Overloaded::operator(); +#undef EFP_ENUM_CASE - template - Overloaded(G&& g, Gs&&... gs) - : F {MatchBranch {forward(g)}}, Overloaded(forward(gs)...) {} -}; +#undef EFP_STAMP2 +#undef EFP_STAMP4 +#undef EFP_STAMP8 +#undef EFP_STAMP16 +#undef EFP_STAMP32 +#undef EFP_STAMP64 +#undef EFP_STAMP128 +#undef EFP_STAMP256 -namespace detail { + template class Case, typename... Args> + using EnumSwitch = _EnumSwitch; - constexpr uint8_t power_2_ceiling(uint8_t n, uint8_t power = 2) { - return (power >= n) ? power : power_2_ceiling(n, power * 2); - } + // todo Maybe support more than 256 alternatives -#define COPY_CASE(i) \ - case i: { \ - using Variant = PackAt < i; \ - new (dest._storage) Variant(src.template get()); \ - break; \ - } + template + using IsWildCard = IsSame>, Tuple<>>; + + // todo zero copy + // Wrapper for wild card to be callable with any arguments + // template + // struct WildCardWrapper: public F { + // // Explicitly define a constructor to accept a lambda or any callable + // template + // WildCardWrapper(G&& g) : F {std::forward(g)} {} + + // // Overload operator() to forward arguments to the callable's operator() + // template + // inline auto operator()(Args&&...) const -> decltype(std::declval()()) { + // return F::operator()(); + // } + // }; + + template + class WildCardWrapper { + public: + // Constructor that initializes the reference. Note the explicit use of a reference to ensure the lambda is not copied. + explicit WildCardWrapper(const F& lambda) : _lambda(lambda) {} -#define MOVE_CASE(i) \ - case i: { \ - using Variant = PackAt < i; \ - new (dest._storage) Variant(efp::move(src.template move())); \ - break; \ + // Overload the function call operator to forward calls to the lambda's own call operator. + template + inline auto operator()(Args&&...) const -> decltype(efp::declval()()) { + return _lambda(); + } + + private: + // A reference to the lambda + const F& _lambda; + }; + + // MatchBranch + // Wrap the wild card with WildCardWrapper, otherwise use the original callable + template + using MatchBranch = Conditional::value, WildCardWrapper, F>; + + template::value>> + WildCardWrapper match_branch(const F& f) { + return WildCardWrapper(f); } -#define DESTROCTOR_CASE(i) \ - case i: { \ - using Variant = PackAt < i; \ - reinterpret_cast(self._storage)->~Variant(); \ - break; \ - } // namespace detail - -#define MATCH_CASE(i) \ - case i: { \ - return overloaded( \ - *(reinterpret_cast \ - < const PackAt*>(outer->_storage)) \ - ); \ - break; \ + // Template specialization for non-wild cards (zero-copy) + template::value>> + const F& match_branch(const F& f) { + return f; } -#define EQUALITY_CASE(i) \ - case i: { \ - using Variant = PackAt < i; \ - return *(reinterpret_cast(self._storage)) \ - == *(reinterpret_cast(other._storage)); \ - break; \ - } // namespace detail + // Overloaded + // Overloaded class for pattern matching + template + struct Overloaded; -#define STAMP2(n, x) x(n) x(n + 1) + template + struct Overloaded: F { + using F::operator(); -#define STAMP4(n, x) \ - STAMP2(n, x) \ - STAMP2(n + 2, x) + Overloaded(const F& f) : F {f} {} + }; -#define STAMP8(n, x) \ - STAMP4(n, x) \ - STAMP4(n + 4, x) + template + struct Overloaded: F, Overloaded { + using F::operator(); + using Overloaded::operator(); -#define STAMP16(n, x) \ - STAMP8(n, x) \ - STAMP8(n + 8, x) + Overloaded(const F& f, const Fs&... fs) : F {f}, Overloaded {fs...} {} + }; -#define STAMP32(n, x) \ - STAMP16(n, x) \ - STAMP16(n + 16, x) + // IsSameUnary + template + struct IsSameUnary { + template + struct Binded { + static const bool value = IsSame::value; + }; + }; -#define STAMP64(n, x) \ - STAMP32(n, x) \ - STAMP32(n + 32, x) + // EnumBase + // Enum is not permitted to hold a reference, but permitted to hold cv-qualified types + template + class EnumBase { + public: + constexpr static uint8_t alt_num = sizeof...(As) + 1; -#define STAMP128(n, x) \ - STAMP64(n, x) \ - STAMP64(n + 64, x) + // constexpr static uint8_t switch_size = power_2_ceiling(alt_num); -#define STAMP256(n, x) \ - STAMP128(n, x) \ - STAMP128(n + 128, x) + // Default constructor initializes the first alternative with default constructor + EnumBase() : _index {0} { + new (reinterpret_cast(&_storage)) A {}; + } - template - struct MatchImpl {}; + EnumBase(const EnumBase& other) : _index {other._index} { + EnumSwitch::call(_index, this, other); + } - template - struct CopyImpl {}; + EnumBase& operator=(const EnumBase& other) { + if (this != &other) { + EnumSwitch::call(_index, this); + _index = other._index; + EnumSwitch::call( + _index, + this, + other + ); + } + return *this; + } - template - struct MoveImpl {}; + EnumBase(EnumBase&& other) : _index {other._index} { + EnumSwitch::call( + _index, + this, + efp::move(other) + ); + } - template - struct DestroctorImpl {}; + EnumBase& operator=(EnumBase&& other) { + if (this != &other) { + EnumSwitch::call(_index, this); + _index = other._index; + EnumSwitch::call( + _index, + this, + efp::move(other) + ); + } + return *this; + } - template - struct EqualityImpl {}; + ~EnumBase() { + EnumSwitch::call(_index, this); + } - template - class EnumBase { - public: - friend struct detail::CopyImpl; - friend struct detail::MoveImpl; - friend struct detail::DestroctorImpl; - friend struct detail::MatchImpl; - friend struct detail::EqualityImpl; - - template - struct IsSameUnary { - template - struct Binded { - static const bool value = IsSame::value; - }; - }; + template, IsSame...>::value>> + EnumBase(const Alt& alt) : _index(AltIndex::value) { + new (reinterpret_cast(_storage)) Alt(alt); + } - template - struct VariantIndex: Find::template Binded, As...> {}; + template, IsSame...>::value>> + EnumBase(Alt&& alt) : _index(AltIndex::value) { + new (reinterpret_cast(_storage)) Alt(efp::move(alt)); + } + // ! Deprecated // Count how many types in the pack are constructible with Args... template struct ConstructibleCount {}; @@ -197,7 +312,7 @@ namespace detail { template using IsUniquelyConstructible = - Bool::template Type::value == 1>; + Bool::template Type::value == 1>; // Base case: no types are constructible template @@ -217,72 +332,13 @@ namespace detail { }; template - using DetermineVariant = EnableIf< + using DetermineAlt = EnableIf< IsUniquelyConstructible::value, - typename FirstConstructible::template Type>; - - // Default constructor will construct the first variant if the first - // variant is default constructible - EnumBase() : _index {0} { - using Variant = PackAt<0, As...>; - new (reinterpret_cast(_storage)) Variant {}; - } - - EnumBase(const EnumBase& other) : _index(other._index) { - detail::CopyImpl::impl(*this, other); - } - - EnumBase& operator=(const EnumBase& other) { - if (this != &other) { - // Should distroy the current variant - detail::DestroctorImpl::impl(*this); - - _index = other._index; - detail::CopyImpl::impl(*this, other); - } - return *this; - } - - EnumBase(EnumBase&& other) noexcept : _index(other._index) { - detail::MoveImpl::impl(*this, efp::move(other)); - // Destructor calling will be done for the moved object. - // Behavior is up to each variant. - } + typename FirstConstructible::template Type>; - EnumBase& operator=(EnumBase&& other) noexcept { - if (this != &other) { - // Should distroy the current variant - detail::DestroctorImpl::impl(*this); - - _index = other._index; - detail::MoveImpl::impl( - *this, - efp::move(other) - ); - // Destructor calling will be done for the moved object. - // Behavior is up to each - } - return *this; - } - - ~EnumBase() { - detail::DestroctorImpl::impl(*this); - } - - // Function name or function type will be automatically converted to - // function pointer type - template, As>::value...)>> - EnumBase(const A& a) : _index(VariantIndex>::value) { - new (reinterpret_cast*>(_storage)) FuncToFuncPtr(a); - } - - // Function name or function type will be automatically converted to - // function pointer type - template, As>::value...)>> - EnumBase(A&& a) : _index(VariantIndex>::value) { - new (reinterpret_cast*>(_storage)) FuncToFuncPtr(efp::move(a)); - } + // ! End of deprecated + // ! Deprecated // Extended constructor // Templated constructor for forwarding arguments to the variants' // constructors @@ -291,18 +347,14 @@ namespace detail { typename... Tail, typename = EnableIf< !(sizeof...(Tail) == 0 && Any...>::value) - && IsUniquelyConstructible::value, - void>> + && IsUniquelyConstructible::value>> EnumBase(Head&& head, Tail&&... args) - : _index(VariantIndex>::value) { + : _index(AltIndex>::value) { // Determine the appropriate variant type based on the argument - using VariantType = DetermineVariant< - Head, - Tail...>; // Implement this based on your logic + using Alt = DetermineAlt; // Implement this based on your logic // Construct the variant in place - new (reinterpret_cast(_storage)) - VariantType(forward(head), forward(args)...); + new (reinterpret_cast(_storage)) Alt(forward(head), forward(args)...); } bool operator==(const EnumBase& other) const { @@ -310,7 +362,11 @@ namespace detail { return false; } - return detail::EqualityImpl::impl(*this, other); + return EnumSwitch::call( + _index, + this, + other + ); } bool operator!=(const EnumBase& other) const { @@ -321,636 +377,269 @@ namespace detail { return _index; } - template - auto get() const -> EnableIf<_any(IsSame::value...), A> { - if (_index != VariantIndex::value) { - throw std::runtime_error("Wrong variant index"); + template + const Alt& get() const { + if (_index != AltIndex::value) { + throw std::runtime_error("Invalid alternative index"); } - return *(reinterpret_cast(_storage)); + return *reinterpret_cast(&_storage); } - template - auto get() -> EnableIf < n> { - if (_index != n) { - throw std::runtime_error("Wrong variant index"); + template + Alt& get() { + if (_index != AltIndex::value) { + throw std::runtime_error("Invalid alternative index"); } - return *(reinterpret_cast*>(_storage)); + return *reinterpret_cast(&_storage); } - template - auto move() const -> EnableIf<_any(IsSame::value...), const A&&> { - if (_index != VariantIndex::value) { - throw std::runtime_error("Wrong variant index"); + template + const PackAt& get() const { + if (_index != i) { + throw std::runtime_error("Invalid alternative index"); } - return efp::move(*(reinterpret_cast(_storage))); + return *reinterpret_cast*>(&_storage); } - template - auto move() -> EnableIf<_any(IsSame::value...), A&&> { - if (_index != VariantIndex::value) { - throw std::runtime_error("Wrong variant index"); + template + PackAt& get() { + if (_index != i) { + throw std::runtime_error("Invalid alternative index"); } - return efp::move(*(reinterpret_cast(_storage))); + return *reinterpret_cast*>(&_storage); } - template - auto move() const -> EnableIf < n const&&> { - if (_index != n) { - throw std::runtime_error("Wrong variant index"); + template + const Alt&& move() const { + if (_index != AltIndex::value) { + throw std::runtime_error("Invalid alternative index"); } - return efp::move(*(reinterpret_cast*>(_storage))); + return efp::move(*reinterpret_cast(&_storage)); } - template - auto move() -> EnableIf < n&&> { - if (_index != n) { - throw std::runtime_error("Wrong variant index"); + template + Alt&& move() { + if (_index != AltIndex::value) { + throw std::runtime_error("Invalid alternative index"); } - return efp::move(*(reinterpret_cast*>(_storage))); + return efp::move(*reinterpret_cast(&_storage)); } - // * Test if all of the branchs have same return type by Common. - // * Check if each F has at least one matching with As. (RelevantBranch) - // * Check if all the variants are matched at least one of Fs or there - // is wildcard at last (Exhaustiveness) - // * Compile time function uint8_t -> uint8_t, which is variant index to - // branch index. - - // * Test if matchs by - // * 1. is there is_same operator (Strip ref of arg type.) - // * 2. is invocable - // * 3. is void arg type - - // * Need type level maybe - // * Need type level find (Type -> bool) -> Types -> int - // * Need type level indexing - - // Arguments implementation will automatically remove the const - // qualifier if there is. - - template - struct IsRelevantBranch { - static constexpr bool value = - _any(IsInvocable::value...) || IsSame, Arguments>::value; - }; - - template - struct AreAllRelevantBranchs { - static constexpr bool value = _all(IsRelevantBranch::value...); - }; - - template - struct IsWildCard: IsSame, Arguments> {}; - - template - struct IsVariantCovered { - static constexpr bool value = - _any(IsWildCard::value...) || _any(IsInvocable::value...); - }; - - template - struct IsExhaustive: False { - static constexpr bool value = _all(IsVariantCovered::value...); - }; - - template - struct IsWellFormed { - static constexpr bool value = true; - // todo count and get last - // (!_any(IsWildCard::value...) && IsWildCard::value) - // || !_any(IsWildCard::value...); - }; - - template - auto match(const Fs&... fs) const -> EnableIf< - AreAllRelevantBranchs::value && IsExhaustive::value - && IsWellFormed::value, - Common...>> { - return detail::MatchImpl::impl( - Overloaded...> {fs...}, - this - ); - } - - private: - // Member variables - alignas(_maximum(alignof(As)...)) uint8_t _storage[_maximum(sizeof(As)...)]; - uint8_t _index; // Current maxinum variant is 256 - }; - - // template specialization could not be in the class scope. - // CopyImpl - - template - struct CopyImpl<2, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP2(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + template + const PackAt&& move() const { + if (_index != i) { + throw std::runtime_error("Invalid alternative index"); } - } - }; - template - struct CopyImpl<4, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP4(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } + return efp::move(*reinterpret_cast*>(&_storage)); } - }; - template - struct CopyImpl<8, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP8(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + template + PackAt&& move() { + if (_index != i) { + throw std::runtime_error("Invalid alternative index"); } - } - }; - template - struct CopyImpl<16, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP16(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } + return efp::move(*reinterpret_cast*>(&_storage)); } - }; - template - struct CopyImpl<32, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP32(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // todo Improve signature to reduce copy of branches - template - struct CopyImpl<64, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP64(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // match + // Pattern matching + template + auto match(const F& f, const Fs&... fs) const + -> decltype(efp::declval, MatchBranch...>>()( + efp::declval() + )) const { + static_assert(PatternCheck::value, "Pattern is not exhaustive"); - template - struct CopyImpl<128, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP128(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + using Pattern = Overloaded, MatchBranch...>; - template - struct CopyImpl<256, As...> { - static void impl(EnumBase& dest, const EnumBase& src) { - switch (src._index) { - STAMP256(0, COPY_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; - - // MoveImpl - template - struct MoveImpl<2, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP2(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; - - template - struct MoveImpl<4, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP4(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } + return EnumSwitch< + alt_num, + Match::template Case, + const EnumBase*, + const Pattern&>::call(_index, this, Pattern {match_branch(f), match_branch(fs)...}); } - }; - template - struct MoveImpl<8, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP8(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + private: + template + struct AltIndex: Find>::template Binded, A, As...> {}; - template - struct MoveImpl<16, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP16(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // Assume that the index is altready bounded + template + struct CopyCase { + using Alt = PackAt; - template - struct MoveImpl<32, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP32(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + static inline void call(EnumBase* self, const EnumBase& other) { + new (self->_storage) Alt {*reinterpret_cast(&other._storage)}; } - } - }; + }; - template - struct MoveImpl<64, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP64(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + template + struct MoveCase { + using Alt = PackAt; - template - struct MoveImpl<128, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP128(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + static inline void call(EnumBase* self, EnumBase&& other) { + new (self->_storage) Alt {efp::move(*reinterpret_cast(&other._storage))}; } - } - }; - - template - struct MoveImpl<256, As...> { - static void impl(EnumBase& dest, EnumBase&& src) { - switch (src._index) { - STAMP256(0, MOVE_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + }; - // DestroctorImpl + template + struct DestroyCase { + using Alt = PackAt; - template - struct DestroctorImpl<2, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP2(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + static inline void call(EnumBase* self) { + reinterpret_cast(&self->_storage)->~Alt(); } - } - }; + }; - template - struct DestroctorImpl<4, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP4(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + template + struct EqCase { + using Alt = PackAt; - template - struct DestroctorImpl<8, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP8(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); + static inline bool call(const EnumBase* self, const EnumBase& other) { + return *reinterpret_cast(&self->_storage) + == *reinterpret_cast(&other._storage); } - } - }; + }; - template - struct DestroctorImpl<16, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP16(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + template + struct Match { + template + struct Case { + using Alt = PackAt; - template - struct DestroctorImpl<32, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP32(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + static inline auto call(const EnumBase* self, const Overloaded& overloaded) + -> decltype(efp::declval()(efp::declval())) { + return overloaded(self->get()); + } + }; + }; - template - struct DestroctorImpl<64, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP64(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // Match branch sanity check - template - struct DestroctorImpl<128, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP128(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // Wild card will be considered as a irrelevant branch + template + using IsRelevantBranch = Any, IsInvocable...>; - template - struct DestroctorImpl<256, As...> { - static void impl(EnumBase& self) { - switch (self._index) { - STAMP256(0, DESTROCTOR_CASE) - default: - throw std::runtime_error("Invalid Enum variant index"); - } - } - }; + // WildCardIffLast + template + struct WildCardIffLast {}; - // MatchImpl + template + struct WildCardIffLast: IsWildCard {}; - template - struct MatchImpl<2, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP2(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + struct WildCardIffLast: WildCardIffLast {}; - template - struct MatchImpl<4, As...> { + // AllButLastAreRelevant template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP4(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + struct AllButLastAreRelevant {}; - template - struct MatchImpl<8, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP8(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + struct AllButLastAreRelevant: True {}; - template - struct MatchImpl<16, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP16(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + struct AllButLastAreRelevant: + Conditional::value, AllButLastAreRelevant, False> {}; - template - struct MatchImpl<32, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP32(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + // todo Make it more stricter only excpeting explicitly invocable branches + // RemoveFirstInvocable + template + struct _RemoveFirstInvocable {}; - template - struct MatchImpl<64, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP64(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + struct _RemoveFirstInvocable> { + using Type = TypeList<>; + }; - template - struct MatchImpl<128, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP128(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + struct _RemoveFirstInvocable> { + using Type = Conditional< + IsInvocable::value, + TypeList, + Prepend>::Type>>; + }; - template - struct MatchImpl<256, As...> { - template - static auto impl(const Overloaded& overloaded, const EnumBase* outer) - -> Common...> { - switch (outer->_index) { - STAMP256(0, MATCH_CASE) - default: - throw std::runtime_error("Invalied Enum variant index"); - } - } - }; + template + using RemoveFirstInvocable = typename _RemoveFirstInvocable::Type; - // EqualityImpl + // Mutual Exhaustiveness + // Check if the alternatives and the branches are mutually exhaustive + template + struct MutExhaust {}; - template - struct EqualityImpl<2, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP2(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + template + struct MutExhaust, TypeList>: IsInvocable {}; - template - struct EqualityImpl<4, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP4(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + template + struct MutExhaust, TypeList>: + Conditional< + sizeof...(Alts) == sizeof...(Fs), + MutExhaust, RemoveFirstInvocable>>, + False> {}; - template - struct EqualityImpl<8, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP8(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + // PatternCheck + template + struct _PatternCheck {}; - template - struct EqualityImpl<16, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP16(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + // No need to check if all the alternatives are covered + // Only need to check if all the branches are relevant + template + struct _PatternCheck { + using Type = AllButLastAreRelevant; + }; - template - struct EqualityImpl<32, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP32(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + // If there is no wild card at the last branch, check if the alternatives and the branches are mutually exhaustive + template + struct _PatternCheck { + using Type = MutExhaust, TypeList>; + }; - template - struct EqualityImpl<64, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP64(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + template + using PatternCheck = + typename _PatternCheck::value, True, False>, Fs...>:: + Type; - template - struct EqualityImpl<128, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP128(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } - }; + // Private member variables + alignas(_maximum({alignof(A), alignof(As)...}) + ) uint8_t _storage[_maximum({sizeof(A), sizeof(As)...})]; - template - struct EqualityImpl<256, As...> { - static bool impl(const EnumBase& self, const EnumBase& other) { - switch (self._index) { - STAMP256(0, EQUALITY_CASE) - default: - throw std::runtime_error("Wrong variant index"); - } - } + uint8_t _index; // Current maxinum variant is 256 }; -#undef MATCH_CASE -#undef STAMP2 -#undef STAMP4 -#undef STAMP8 -#undef STAMP16 -#undef STAMP32 -#undef STAMP64 -#undef STAMP128 -#undef STAMP256 } // namespace detail template class Enum: public detail::EnumBase { +public: using Base = detail::EnumBase; using Base::Base; }; -template -auto match(const Enum& x, const Fs&... fs) -> EnableIf< - _all(Enum::template IsRelevantBranch::value...) - && Enum::template IsExhaustive::value - && Enum::template IsWellFormed::value, - Common...>> { - return Enum::template MatchImpl::impl(Overloaded {fs...}, &x); -} - // EnumAt namespace detail { template - struct EnumAtImpl {}; + struct _EnumAt {}; template - struct EnumAtImpl> { + struct _EnumAt> { using Type = PackAt; }; } // namespace detail template -using EnumAt = typename detail::EnumAtImpl::Type; +using EnumAt = typename detail::_EnumAt::Type; } // namespace efp -#endif \ No newline at end of file +#endif // EFP_ENUM_HPP \ No newline at end of file diff --git a/include/efp/io.hpp b/include/efp/io.hpp index 1615a76..241e9e6 100644 --- a/include/efp/io.hpp +++ b/include/efp/io.hpp @@ -18,7 +18,7 @@ class File { FILE* file = std::fopen(path, mode); if (file) { - return File {file, mode}; + return efp::move(File {file, mode}); } return nothing; @@ -28,7 +28,7 @@ class File { FILE* file = std::fopen(path.c_str(), mode); if (file) { - return File {file, mode}; + return efp::move(File {file, mode}); } return nothing; diff --git a/include/efp/maybe.hpp b/include/efp/maybe.hpp index 533624b..f58c312 100644 --- a/include/efp/maybe.hpp +++ b/include/efp/maybe.hpp @@ -44,7 +44,7 @@ class Enum: public detail::EnumBase { } template - auto fmap(const F& f) -> Enum> { + auto fmap(const F& f) -> Enum> { if (has_value()) return f(value()); else @@ -70,7 +70,7 @@ struct ElementImpl> { // functor template -auto fmap(const F& f, const Maybe& ma) -> Maybe> { +auto fmap(const F& f, const Maybe& ma) -> Maybe> { if (ma) return f(ma.value()); else @@ -84,7 +84,7 @@ auto pure(const Element& a) -> EnableIf::value, A> { } template -auto ap(const Maybe& mf, const Maybe& ma) -> Maybe> { +auto ap(const Maybe& mf, const Maybe& ma) -> Maybe> { if (mf && ma) return mf.value()(ma.value()); else @@ -95,7 +95,7 @@ auto ap(const Maybe& mf, const Maybe& ma) -> Maybe> { template auto bind(const Maybe& ma, const F& f) - -> EnableIf>::value, CallReturn> { + -> EnableIf>::value, InvokeResult> { if (ma) return f(ma.value()); else @@ -104,7 +104,7 @@ auto bind(const Maybe& ma, const F& f) template auto operator>>=(const Maybe& ma, const F& f) - -> EnableIf>::value, CallReturn> { + -> EnableIf>::value, InvokeResult> { if (ma) return f(ma.value()); else diff --git a/include/efp/meta.hpp b/include/efp/meta.hpp index f6894e8..76a6fef 100644 --- a/include/efp/meta.hpp +++ b/include/efp/meta.hpp @@ -6,7 +6,6 @@ namespace efp { // Unit - struct Unit { constexpr bool operator==(const Unit&) const noexcept { return true; @@ -20,7 +19,6 @@ struct Unit { constexpr Unit unit; // CtConst - template using CtConst = std::integral_constant; @@ -55,30 +53,24 @@ constexpr auto operator/(CtConst, CtConst) -> CtConst using Bool = CtConst; // True - using True = CtConst; // False - using False = CtConst; // Int - template using Int = CtConst; // Size - template using Size = CtConst; // IsCtConst - template struct IsCtConst { static constexpr bool value = false; @@ -96,74 +88,21 @@ template struct IsCtConst: IsCtConst {}; // AlwaysFalse - template struct AlwaysFalse: False {}; // EnableIf - template using EnableIf = typename std::enable_if::type; // Conditionl - template using Conditional = typename std::conditional::type; -// All - -template -struct All {}; - -// Base case: When no types are left, return true. -template<> -struct All<>: True {}; - -// Recursive case: Check the first type, and recurse for the rest. -template -struct All: Bool::value> {}; - -// Any - -template -struct Any {}; - -// Base case: When no types are left, return false. -template<> -struct Any<>: False {}; - -// Recursive case: Check the first type, and recurse for the rest. -template -struct Any: Bool::value> {}; - -// Min - -template -struct Min: Min> {}; - -template -struct Min: Conditional {}; - -template -struct Min: Head {}; - -// Max - -template -struct Max: Max> {}; - -template -struct Max: Conditional= Tail::value, Head, Tail> {}; - -template -struct Max: Head {}; - // size_of_ptr_v - constexpr auto size_of_ptr_v = sizeof(void*); // NumericLimits - template using NumericLimits = std::numeric_limits; @@ -172,109 +111,93 @@ using NumericLimits = std::numeric_limits; // It's also the way Haskell and Rust do // op_neg - template constexpr A op_neg(const A& a) { return -a; } // op_eq - template constexpr bool op_eq(const A& lhs, const A& rhs) { return lhs == rhs; } // op_neq - template constexpr bool op_neq(const A& lhs, const A& rhs) { return lhs != rhs; } // op_gt - template constexpr bool op_gt(const A& lhs, const A& rhs) { return lhs > rhs; } // op_lt - template constexpr bool op_lt(const A& lhs, const A& rhs) { return lhs < rhs; } // op_geq - template constexpr bool op_geq(const A& lhs, const A& rhs) { return lhs >= rhs; } // op_leq - template constexpr bool op_leq(const A& lhs, const A& rhs) { return lhs <= rhs; } // op_not - -constexpr bool op_not(const bool b) { +constexpr bool op_not(const bool& b) { return !b; } // op_and - -constexpr bool op_and(const bool lhs, const bool rhs) { +constexpr bool op_and(const bool& lhs, const bool& rhs) { return lhs && rhs; } // op_or - -constexpr bool op_or(const bool lhs, const bool rhs) { +constexpr bool op_or(const bool& lhs, const bool& rhs) { return lhs || rhs; } // op_add - template constexpr A op_add(const A& lhs, const A& rhs) { return lhs + rhs; } // op_sub - template constexpr A op_sub(const A& lhs, const A& rhs) { return lhs - rhs; } // op_mul - template constexpr A op_mul(const A& lhs, const A& rhs) { return lhs * rhs; } // op_div - template constexpr A op_div(const A& lhs, const A& rhs) { return lhs / rhs; } // op_mod - template constexpr A op_mod(const A& lhs, const A& rhs) { return lhs % rhs; } // bound_v - template constexpr auto bound_v(const A& lower, const B& upper, const C& x) -> decltype((x > upper) ? (upper) : ((x < lower) ? lower : x)) { @@ -282,14 +205,12 @@ constexpr auto bound_v(const A& lower, const B& upper, const C& x) } // max - template constexpr A max(const A& lhs, const A& rhs) { return lhs > rhs ? lhs : rhs; } // min - template constexpr A min(const A& lhs, const A& rhs) { return lhs < rhs ? lhs : rhs; @@ -297,20 +218,20 @@ constexpr A min(const A& lhs, const A& rhs) { // Foldl // Maybe just recursive constexpr template function could be enough +// ! deprecated because of performance issue +// namespace detail { +// template class F, typename A, typename... Bs> +// struct FoldlImpl {}; -namespace detail { - template class F, typename A, typename... Bs> - struct FoldlImpl {}; +// template class F, typename A, typename B> +// struct FoldlImpl: F::Type {}; - template class F, typename A, typename B> - struct FoldlImpl: F::Type {}; +// template class F, typename A, typename B0, typename B1, typename... Bs> +// struct FoldlImpl: FoldlImpl::Type, B1, Bs...> {}; +// } // namespace detail - template class F, typename A, typename B0, typename B1, typename... Bs> - struct FoldlImpl: FoldlImpl::Type, B1, Bs...> {}; -} // namespace detail - -template class F, typename A, typename... Bs> -using Foldl = typename detail::FoldlImpl::Type; +// template class F, typename A, typename... Bs> +// using Foldl = typename detail::FoldlImpl::Type; // IsSame @@ -320,7 +241,7 @@ using IsSame = std::is_same; // PackAt namespace detail { - template + template struct PackAtImpl { using Type = void*; }; @@ -330,75 +251,64 @@ namespace detail { using Type = Head; }; - template + template struct PackAtImpl: PackAtImpl {}; } // namespace detail -template +template using PackAt = typename detail::PackAtImpl::Type; // Find namespace detail { - // FindHelperValue - - template - struct FindHelperValue { - static constexpr uint8_t value = n; - }; - // FindImpl - template class P, typename... Args> struct FindImpl {}; template class P, typename Head, typename... Tail> struct FindImpl: - Conditional::value, FindHelperValue, FindImpl> {}; + Conditional::value, CtConst, FindImpl> {}; } // namespace detail +// Find the first type in the list that satisfies the predicate P template class P, typename... Args> struct Find: detail::FindImpl<0, P, Args...> {}; // LvalueRefAdded - template using LvalueRefAdded = typename std::add_lvalue_reference::type; // RvalueRefAdded - template using RvalueRefAdded = typename std::add_rvalue_reference::type; // declval - template constexpr RvalueRefAdded declval() noexcept; -// CallReturn +// InvokeResult // Check the C++ standard version #if __cplusplus >= 201703L // C++17 or later, use std::invoke_result template -using CallReturn = typename std::invoke_result::type; +using InvokeResult = typename std::invoke_result::type; #else // Before C++17, use the custom implementation namespace detail { template - struct CallReturnImpl { + struct InvokeResultImpl { using Type = decltype(declval()(declval()...)); }; } // namespace detail template -using CallReturn = typename detail::CallReturnImpl::Type; +using InvokeResult = typename detail::InvokeResultImpl::Type; #endif // HasCallOperator - template class HasCallOperator { typedef char one; @@ -415,13 +325,13 @@ class HasCallOperator { }; // IsInvocable +// ? Is Custom implementation faster? +// #if __cplusplus >= 201703L // If C++17 or later, use std::is_invocable -#if __cplusplus >= 201703L // If C++17 or later, use std::is_invocable - -template -using IsInvocable = std::is_invocable; +// template +// using IsInvocable = std::is_invocable; -#else // If earlier than C++17, use custom IsInvocable +// #else // If earlier than C++17, use custom IsInvocable template struct IsInvocable { @@ -436,15 +346,13 @@ struct IsInvocable { static constexpr bool value = decltype(check(0))::value; }; -#endif +// #endif // IsFunction - template using IsFunction = std::is_function; // FuncToFuncPtr - namespace detail { // Base template @@ -486,7 +394,6 @@ template using FuncToFuncPtr = typename detail::FuncToFuncPtrImpl::Type; // TupleLeaf - template class TupleLeaf { public: @@ -516,12 +423,10 @@ class TupleLeaf { }; // IndexSequence - template struct IndexSequence {}; // MakeIndexSequenceImpl - template struct MakeIndexSequenceImpl: MakeIndexSequenceImpl {}; @@ -531,12 +436,10 @@ struct MakeIndexSequenceImpl<0, ns...> { }; // MakeIndexSequence - template using MakeIndexSequence = typename MakeIndexSequenceImpl::Type; // IndexSequenceFor - template using IndexSequenceFor = MakeIndexSequence; @@ -554,14 +457,13 @@ namespace detail { protected: template auto match_impl(const F& f) const - -> EnableIf::value, CallReturn> { + -> EnableIf::value, InvokeResult> { return f(TupleLeaf>::get()...); } }; } // namespace detail // Tuple - template class Tuple: public detail::TupleImpl, As...> { public: @@ -578,7 +480,7 @@ class Tuple: public detail::TupleImpl, As...> { } template - auto match(const F& f) const -> EnableIf::value, CallReturn> { + auto match(const F& f) const -> EnableIf::value, InvokeResult> { return detail::TupleImpl, As...>::match_impl(f); } @@ -586,7 +488,6 @@ class Tuple: public detail::TupleImpl, As...> { }; // Pair - template using Pair = Tuple; @@ -613,7 +514,6 @@ auto p(Tuple& tpl) -> PackAt& { } // fst - template auto fst(const Tuple& tpl) -> const PackAt<0, As...>& { return tpl.template get<0>(); @@ -625,7 +525,6 @@ auto fst(Tuple& tpl) -> PackAt<0, As...>& { } // snd - template auto snd(const Tuple& tpl) -> const PackAt<1, As...>& { return tpl.template get<1>(); @@ -677,14 +576,12 @@ bool operator!=(const Tuple& lhs, const Tuple& rhs) { } // tuple - template auto tuple(const As&... as) -> Tuple...> { return Tuple...> {as...}; } // TupleAt - namespace detail { template struct TupleAtImpl {}; @@ -737,7 +634,7 @@ namespace detail { template struct ReturnFromArgumentImpl> { - using Type = CallReturn; + using Type = InvokeResult; }; } // namespace detail @@ -818,50 +715,43 @@ namespace detail { template using Return = typename detail::ReturnImpl::Type; -// apply - namespace detail { template - Return apply_impl(const F& f, const Tuple& tpl, IndexSequence) { + Return _apply(const F& f, const Tuple& tpl, IndexSequence) { return f(get(tpl)...); } } // namespace detail +// apply template< typename F, typename... As, typename = EnableIf, Tuple>::value, void>> Return apply(const F& f, const Tuple& tpl) { - return detail::apply_impl(f, tpl, IndexSequenceFor {}); + return detail::_apply(f, tpl, IndexSequenceFor {}); } // PointerRemoved - template using PointerRemoved = typename std::remove_pointer::type; // ReferenceRemoved - template using ReferenceRemoved = typename std::remove_reference::type; // ConstRemoved - template using ConstRemoved = typename std::remove_const::type; // VoletileRemoved - template using VoletileRemoved = typename std::remove_volatile::type; // CVRemoved - template using CVRemoved = VoletileRemoved>; // CVRefRemoved - template using CVRefRemoved = CVRemoved>; @@ -870,12 +760,10 @@ template using Decay = typename std::decay::type; // IsConst - template using IsConst = std::is_const; // Void - namespace detail { template struct VoidImpl { @@ -895,7 +783,6 @@ template struct IsLvalueReference: True {}; // forward - template constexpr A&& forward(ReferenceRemoved& a) noexcept { return static_cast(a); @@ -908,14 +795,12 @@ constexpr A&& forward(ReferenceRemoved&& a) noexcept { } // move - template constexpr ReferenceRemoved&& move(A&& a) { return static_cast&&>(a); } // swap - template void swap(A& a, A& b) { A temp = efp::move(a); @@ -931,95 +816,85 @@ struct IsDefaultConstructible: False {}; template struct IsDefaultConstructible: True {}; -// _foldl - -template -constexpr A _foldl(F f, A a) { - return a; -} +// InitializerList -#if __cplusplus >= 201703L -// C++17 or later, use a loop for foldl -template -constexpr A _foldl(F f, A a, Bs... bs) { - // Convert parameter pack to array for iteration - A arr[] = {static_cast(bs)...}; +template +using InitializerList = std::initializer_list; - for (auto& element : arr) { - a = f(a, element); +namespace detail { + template + constexpr A foldl(const F& f, A a, It begin, It end) { + return begin == end ? a : foldl(f, f(a, *begin), begin + 1, end); } +} // namespace detail - return a; +// _foldl :: (A -> B -> A) -> A -> [B] -> A +template +constexpr A _foldl(const F& f, A a, const B (&bs)[n]) { + return detail::foldl(f, a, bs, bs + n); } -#else -// Before C++17, recursive implementation -template -constexpr A _foldl(F f, A a, B b, Bs... bs) { - return _foldl(f, f(efp::forward(a), efp::forward(b)), efp::forward(bs)...); -} -#endif -// _all - -template -constexpr bool _all(Args... args) { - return _foldl(op_and, true, args...); +// _all :: [Bool] -> Bool +template +constexpr bool _all(const bool (&bs)[n]) { + return _foldl(op_and, true, bs); } -// _any - -template -constexpr bool _any(Args... args) { - return _foldl(op_or, false, args...); +// _any :: [Bool] -> Bool +template +constexpr bool _any(const bool (&bs)[n]) { + return _foldl(op_or, false, bs); } -// _maximum -// cf) since the function is defined as foldr, the result follows the type of first argument. - -template -constexpr A _maximum(A a, As... as) { - return _foldl(max, a, as...); +// _maximum :: [A] -> A +template +constexpr A _maximum(const A (&as)[n]) { + return _foldl(max, NumericLimits::min(), as); } -// _minimum -// cf) since the function is defined as foldr, the result follows the type of first argument. - -template -constexpr A _minimum(A a, As... as) { - return _foldl(min, a, as...); +// _minimum :: [A] -> A +template +constexpr A _minimum(const A (&as)[n]) { + return _foldl(min, NumericLimits::max(), as); } -// _sum +// _sum :: [A] -> A +template +constexpr A _sum(const A (&as)[n]) { + return _foldl(op_add, 0, as); +} -template -constexpr A _sum(A a, As... as) { - return _foldl(op_add, a, as...); +// _product :: [A] -> A +template +constexpr A _product(const A (&as)[n]) { + return _foldl(op_mul, 1, as); } -// _product +// All +template +struct All: Bool<_all({A::value, As::value...})> {}; +// Any template -constexpr A _product(A a, As... as) { - return _foldl(op_mul, a, as...); -} +struct Any: Bool<_any({A::value, As::value...})> {}; -// InitializerList +// Minimum +template +struct Minimum: CtConst {}; -template -using InitializerList = std::initializer_list; +// Maximum +template +struct Maximum: CtConst {}; // Common - template using Common = typename std::common_type::type; // DebugType - template struct DebugType; // Intentionally undefined // IsConstructible - template using IsConstructible = Bool::value>; @@ -1030,8 +905,7 @@ using IsConstructible = Bool::value>; // alignas(Align) char data[Len]; // }; -// Storage - +// RawStorage template struct RawStorage { static_assert(sizeof(A) % alignof(A) == 0, "Size of A must be a multiple of its alignment"); @@ -1064,6 +938,25 @@ struct RawStorage { } }; +// TypeList +template +struct TypeList {}; + +// Helper to prepend a type to a TypeList +namespace detail { + template + struct PrependImpl {}; + + template + struct PrependImpl> { + using Type = TypeList; + }; +} // namespace detail + +// Prepend +template +using Prepend = typename detail::PrependImpl::Type; + } // namespace efp #endif \ No newline at end of file diff --git a/include/efp/prelude.hpp b/include/efp/prelude.hpp index f89c1a9..a66717e 100644 --- a/include/efp/prelude.hpp +++ b/include/efp/prelude.hpp @@ -13,6 +13,7 @@ namespace efp { // todo concat, concat_map +// id :: A -> A template constexpr A id(const A& a) { return a; @@ -48,8 +49,7 @@ struct Composed { } }; -// compose - +// compose :: (B -> C) -> (A -> B) -> A -> C template auto compose(const F& f, const G& g) -> Composed { return Composed(f, g); @@ -65,18 +65,17 @@ auto compose(const F& f, const Fs&... fs) -> Composed { template size_t _min_length(const As& as, const Ass&... ass) { static_assert( - All, IsSequence...>::value, - "All types must be sequence types." + _all({IsSequence::value, IsSequence::value...}), + "Arguments should implement sequence trait." ); - return _minimum(static_cast(length(as)), length(ass)...); + return _minimum({static_cast(length(as)), static_cast(length(ass))...}); } -// for_each - +// for_each :: (A -> void) -> [A] -> void template&...)> void for_each(const F& f, const Ass&... ass) { - static_assert(All...>::value, "All types must be sequence types."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); const size_t res_length = _min_length(ass...); @@ -85,11 +84,10 @@ void for_each(const F& f, const Ass&... ass) { } } -// for_each_mut - +// for_each_mut :: (A -> void) -> [A] -> void template&...)> void for_each_mut(const F& f, Ass&... ass) { - static_assert(All...>::value, "All types must be sequence types."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); const size_t res_length = _min_length(ass...); @@ -100,26 +98,26 @@ void for_each_mut(const F& f, Ass&... ass) { template using NAryReturn = Conditional< - All...>::value, - Array...>::value>, + _all({IsStaticSize::value...}), + Array::value...})>, Conditional< - All...>::value, - ArrVec...>::value>, + _all({IsStaticCapacity::value...}), + ArrVec::value...})>, Vector>>; // MapReturn template -using MapReturn = NAryReturn...>, Ass...>; - -// map +using MapReturn = NAryReturn...>, Ass...>; +// map :: (A -> B) -> [A] -> [B] template auto map(const F& f, const Ass&... ass) -> MapReturn { - static_assert(All...>::value, "All arguments should implement Sequence trait."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); MapReturn res {}; const size_t res_len = _min_length(ass...); + if (CtSize>::value == dyn) { res.resize(res_len); } @@ -139,8 +137,7 @@ using FilterReturn = Conditional< ArrVec, CtCapacity::value>, Vector>>; -// filter - +// filter :: (A -> Bool) -> [A] -> [A] template&)> auto filter(const F& f, const As& as) -> FilterReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); @@ -150,6 +147,7 @@ auto filter(const F& f, const As& as) -> FilterReturn { for (size_t i = 0; i < res_len; ++i) { const auto& a = nth(i, as); + if (f(a)) { res.push_back(a); } @@ -158,28 +156,26 @@ auto filter(const F& f, const As& as) -> FilterReturn { return res; } -// foldl +// foldl :: (A -> B -> A) -> A -> [B] -> A +template&)> +auto foldl(const F& f, const A& init, const Bs& bs) -> A { + static_assert(IsSequence::value, "Argument should implement sequence trait."); -template&)> -R foldl(const F& f, const R& init, const As& as) { - static_assert(IsSequence::value, "Argument should implement sequence trait."); + A res = init; - R res = init; - - for (size_t i = 0; i < length(as); ++i) { - res = f(res, nth(i, as)); + for (size_t i = 0; i < length(bs); ++i) { + res = f(res, nth(i, bs)); } return res; } -// foldr - -template&, const R&)> -R foldr(const F& f, const R& init, const As& as) { +// foldr :: (A -> B -> B) -> B -> [A] -> B +template&, const B&)> +auto foldr(const F& f, const B& init, const As& as) -> B { static_assert(IsSequence::value, "Argument should implement sequence trait."); - R res = init; + B res = init; for (size_t i = length(as) - 1; i != -1; --i) { res = f(nth(i, as), res); @@ -193,12 +189,12 @@ R foldr(const F& f, const R& init, const As& as) { namespace detail { template struct FromFunctionReturnImpl { - using Type = Vector>; + using Type = Vector>; }; template struct FromFunctionReturnImpl, F> { - using Type = Array, n>; + using Type = Array, n>; }; } // namespace detail @@ -207,8 +203,7 @@ namespace detail { template using FromFunctionReturn = typename detail::FromFunctionReturnImpl::Type; -// from_function - +// from_function :: (Size -> A) -> Size -> [A] template auto from_function(const N& length, const F& f) -> FromFunctionReturn { FromFunctionReturn res {}; @@ -231,17 +226,17 @@ void execute_pack(Args... args) {} template using AppendReturn = Conditional< - All...>::value, - Array...>, _sum(CtSize::value...)>, + _all({IsStaticSize::value...}), + Array...>, _sum({CtSize::value...})>, Conditional< - All...>::value, - ArrVec...>, _sum(CtCapacity::value...)>, + _all({IsStaticCapacity::value...}), + ArrVec...>, _sum({CtCapacity::value...})>, Vector...>>>>; namespace detail { template Unit append_impl(size_t& idx, As& as, const Bs& bs) { - const auto seq_length = bs.size(); // Assuming `size` method is available + const auto seq_length = length(bs); for (size_t i = 0; i < seq_length; ++i) { nth(idx, as) = nth(i, bs); @@ -252,20 +247,21 @@ namespace detail { } } // namespace detail +// append :: [A] -> [A] ... -> [A] template auto append(const As& as, const Ass&... ass) -> AppendReturn { static_assert( - All, IsSequence...>::value, - "All types must be sequence types." + _all({IsSequence::value, IsSequence::value...}), + "Arguments should implement sequence trait." ); AppendReturn res {}; if (CtSize>::value == dyn) { - res.resize(_sum(static_cast(length(as)), length(ass)...)); + res.resize(_sum({static_cast(length(as)), static_cast(length(ass))...})); } - size_t idx {0}; + size_t idx = 0; execute_pack(detail::append_impl(idx, res, as), detail::append_impl(idx, res, ass)...); return res; @@ -308,6 +304,7 @@ auto concat(const Ass ass) -> ConcatReturn { if (!IsStaticSize>::value) { size_t res_len = 0; + for (size_t i = 0; i < ass_len; ++i) { res_len += length(nth(i, ass)); } @@ -332,12 +329,16 @@ auto concat(const Ass ass) -> ConcatReturn { template using IntercalateReturn = Conditional< - All, IsStaticSize, IsStaticSize>>::value, + _all({IsStaticSize::value, IsStaticSize::value, IsStaticSize>::value}), Array< Element>, CtSize::value*(CtSize>::value + CtSize::value) - CtSize::value>, Conditional< - All, IsStaticCapacity, IsStaticCapacity>>::value, + _all( + {IsStaticCapacity::value, + IsStaticCapacity::value, + IsStaticCapacity>::value} + ), ArrVec< Element>, CtCapacity::value*(CtCapacity>::value + CtCapacity::value) @@ -349,7 +350,7 @@ using IntercalateReturn = Conditional< template auto intercalate(const As& delimeter, const Ass& ass) -> IntercalateReturn { static_assert( - All, IsSequence, IsSequence>>::value, + _all({IsSequence::value, IsSequence::value, IsSequence>::value}), "Argument should implement sequence trait." ); @@ -357,17 +358,20 @@ auto intercalate(const As& delimeter, const Ass& ass) -> IntercalateReturn>::value == dyn) { size_t result_len = 0; + for (size_t i = 0; i < ass_len; ++i) { result_len += length(nth(i, ass)); } - if (ass_len != 0) + if (ass_len != 0) { result_len = result_len + (ass_len - 1) * delimeter_len; + } result.resize(result_len); } @@ -380,6 +384,7 @@ auto intercalate(const As& delimeter, const Ass& ass) -> IntercalateReturn&...)> void for_each_with_index(const F& f, const Ass&... seqs) { - static_assert(All...>::value, "All arguments should implement Sequence trait."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); const auto min_len = _min_length(seqs...); for (size_t i = 0; i < min_len; ++i) { @@ -418,7 +423,7 @@ void for_each_with_index(const F& f, const Ass&... seqs) { template&...)> void for_each_with_index_mut(const F& f, Ass&... seqs) { - static_assert(All...>::value, "All arguments should implement Sequence trait."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); const size_t min_len = _min_length(seqs...); for (size_t i = 0; i < min_len; ++i) { @@ -441,7 +446,7 @@ template< typename F = void (*)(const Element&, const Element&...)> void cartesian_for_each(const F& f, const As& as, const Ass&... ass) { static_assert( - All, IsSequence...>::value, + _all({IsSequence::value, IsSequence::value...}), "All arguments should be instance of Sequence trait." ); @@ -465,7 +470,7 @@ void cartesian_for_each_mut(const F& f, As& as) { template&, Element&...)> void cartesian_for_each_mut(const F& f, As& as, Ass&... ass) { static_assert( - All, IsSequence...>::value, + _all({IsSequence::value, IsSequence::value...}), "All arguments should be instance of Sequence trait." ); @@ -483,18 +488,18 @@ void cartesian_for_each_mut(const F& f, As& as, Ass&... ass) { template using MapWithIndexReturn = Conditional< - All...>::value, - Array...>, Min...>::value>, + _all({IsStaticSize::value...}), + Array...>, _minimum({CtSize::value...})>, Conditional< - All...>::value, - ArrVec...>, Min...>::value>, - Vector...>>>>; + _all({IsStaticCapacity::value...}), + ArrVec...>, _minimum({CtCapacity::value...})>, + Vector...>>>>; // map_with_index template&...)> auto map_with_index(const F& f, const Ass&... ass) -> MapWithIndexReturn { - static_assert(All...>::value, "All arguments should implement Sequence trait."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); auto res = MapWithIndexReturn {}; const auto res_len = _min_length(ass...); @@ -514,12 +519,12 @@ auto map_with_index(const F& f, const Ass&... ass) -> MapWithIndexReturn using CartesianMapReturn = Conditional< - All...>::value, - Array...>, _product(CtSize::value...)>, + _all({IsStaticSize::value...}), + Array...>, _product({CtSize::value...})>, Conditional< - All...>::value, - ArrVec...>, _product(CtCapacity::value...)>, - Vector...>>>>; + _all({IsStaticCapacity::value...}), + ArrVec...>, _product({CtCapacity::value...})>, + Vector...>>>>; // cartesian_map @@ -527,15 +532,16 @@ using CartesianMapReturn = Conditional< template&...)> auto cartesian_map(const F& f, const Ass&... ass) -> CartesianMapReturn { - static_assert(All...>::value, "All arguments should implement Sequence trait."); + static_assert(_all({IsSequence::value...}), "Arguments should implement sequence trait."); auto res = CartesianMapReturn {}; + if (CtSize>::value == dyn) { - res.resize(_product(static_cast(length(ass))...)); + res.resize(_product({static_cast(length(ass))...})); } size_t i = 0; - const auto inner = [&](const Element&... xss) { res[i++] = f(xss...); }; + const auto inner = [&](const Element&... xss) { nth(i++, res) = f(xss...); }; cartesian_for_each(inner, ass...); @@ -553,7 +559,6 @@ template void cartesian_for_index(const F& f, size_t n, const Ints&... is) { for (size_t i = 0; i < n; ++i) { const auto inner = [&](const Ints&... is) { f(i, is...); }; - cartesian_for_index(inner, is...); } } @@ -566,9 +571,9 @@ template auto head(const As& as) -> const Element& { static_assert(IsSequence::value, "Argument should implement sequence trait."); - // assert(!as.empty()); // Ensure the sequence is not empty. - if (as.empty()) + if (as.empty()) { throw std::runtime_error("Sequence should not be empty"); + } return nth(0, as); } @@ -599,9 +604,9 @@ template auto tail(const A& as) -> TailReturn { static_assert(IsSequence::value, "Argument should be an instance of Sequence trait."); - // assert(length(as) > 0); // Ensure the sequence is not empty. - if (length(as) == 0) + if (length(as) == 0) { throw std::runtime_error("Sequence should not be empty"); + } return {data(as) + 1, length(as) - 1}; } @@ -610,9 +615,9 @@ template auto tail(A& as) -> TailReturn { static_assert(IsSequence::value, "Argument should be an instance of Sequence trait."); - // assert(length(as) > 0); // Ensure the sequence is not empty. - if (length(as) == 0) + if (length(as) == 0) { throw std::runtime_error("Sequence should not be empty"); + } return {data(as) + 1, length(as) - 1}; } @@ -644,9 +649,9 @@ template auto init(const As& as) -> InitReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); - // assert(length(as) > 0); // Ensure the sequence is not empty. - if (length(as) == 0) + if (length(as) == 0) { throw std::runtime_error("Sequence should not be empty"); + } return {data(as), length(as) - 1}; } @@ -655,9 +660,9 @@ template auto init(As& as) -> InitReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); - // assert(length(as) > 0); // Ensure the sequence is not empty. - if (length(as) == 0) + if (length(as) == 0) { throw std::runtime_error("Sequence should not be empty"); + } return {data(as), length(as) - 1}; } @@ -670,11 +675,11 @@ template auto last(const As& as) -> const Element& { static_assert(IsSequence::value, "Argument should implement sequence trait."); - // assert(length(as) > 0); // Ensure the sequence is not empty. - if (length(as) == 0) + if (length(as) == 0) { throw std::runtime_error("Sequence should not be empty"); + } - return as[length(as) - 1]; + return nth(length(as) - 1, as); } // is_null @@ -703,7 +708,10 @@ namespace detail { template struct TakeUnsafeReturnImpl, As, is_const> { - // todo Static assert for invalid size + static_assert( + n <= CtSize::value, + "Size should be smaller than the length of the sequence." + ); using Type = Conditional, n>, ArrayView, n>>; @@ -717,8 +725,8 @@ using TakeUnsafeReturn = typename detail::TakeUnsafeReturnImpl: // take_unsafe -// !Should not put n longer than the length. Check should be done by the caller -// Let's make unsafe version as well +// ! Should not put n longer than the length. Check should be done by the caller + template auto take_unsafe(N n, const As& as) -> TakeUnsafeReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); @@ -773,8 +781,6 @@ using TakeReturn = typename detail::TakeReturnImpl::Type; // take -// !Should not put n longer than the length. Check should be done by the caller -// Let's make unsafe version as well template auto take(N n, const As& as) -> TakeReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); @@ -795,7 +801,7 @@ auto take(N n, As& as) -> TakeReturn { ); // Safeguarding against n > length(as) } -// Dr)opUnsafeReturnImpl +// DropUnsafeReturnImpl namespace detail { template @@ -896,7 +902,8 @@ auto drop(N n, const As& as) -> DropReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); const size_t as_len = length(as); - size_t bound_drop_size = (n > as_len) ? as_len : n; // Ensuring n doesn't exceed the size of as + const size_t bound_drop_size = + (n > as_len) ? as_len : n; // Ensuring n doesn't exceed the size of as return DropReturn(data(as) + bound_drop_size, as_len - bound_drop_size); } @@ -906,7 +913,8 @@ auto drop(N n, As& as) -> DropReturn { static_assert(IsSequence::value, "Argument should implement sequence trait."); const size_t as_len = length(as); - size_t bound_drop_size = (n > as_len) ? as_len : n; // Ensuring n doesn't exceed the size of as + const size_t bound_drop_size = + (n > as_len) ? as_len : n; // Ensuring n doesn't exceed the size of as return DropReturn(data(as) + bound_drop_size, as_len - bound_drop_size); } @@ -961,8 +969,6 @@ auto slice(S start, E end, As& as) -> SliceReturn { return SliceReturn(data(as) + start, end - start); } -// todo Add test for take_while and drop_while - // take_while template @@ -1016,8 +1022,9 @@ bool elem(const Element& a, const As& as) { const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (nth(i, as) == a) + if (nth(i, as) == a) { return true; + } } return false; @@ -1032,8 +1039,9 @@ Maybe elem_index(const Element& a, const As& as) { const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (nth(i, as) == a) + if (nth(i, as) == a) { return i; + } } return nothing; @@ -1055,8 +1063,9 @@ auto elem_indices(const Element& a, const As& as) -> ElemIndicesReturn { const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (a == nth(i, as)) + if (a == nth(i, as)) { res.push_back(i); + } } return res; @@ -1066,11 +1075,14 @@ auto elem_indices(const Element& a, const As& as) -> ElemIndicesReturn { template&)> bool find(const F& f, const As& as) { + static_assert(IsSequence::value, "Argument should implement sequence trait."); + const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (f(nth(i, as))) + if (f(nth(i, as))) { return true; + } } return false; @@ -1085,8 +1097,9 @@ auto find_index(const F& f, const As& as) -> Maybe { const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (f(nth(i, as))) + if (f(nth(i, as))) { return i; + } } return nothing; @@ -1108,11 +1121,14 @@ auto find_indices(const F& f, const As& as) -> FindIndicesReturn { const auto as_len = length(as); for (size_t i = 0; i < as_len; ++i) { - if (f(nth(i, as))) + if (f(nth(i, as))) { res.push_back(i); + } } return res; } + } // namespace efp + #endif \ No newline at end of file diff --git a/include/efp/sequence.hpp b/include/efp/sequence.hpp index 5d57b53..0bd1129 100644 --- a/include/efp/sequence.hpp +++ b/include/efp/sequence.hpp @@ -31,6 +31,20 @@ class Array { using CtSize = Size; using CtCapacity = Size; + // STL compatible types + using value_type = Element; + using allocator_type = std::allocator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = value_type*; + using const_iterator = const value_type*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + Array() { // By definition all of the data in Array should be valid for (size_t i = 0; i < ct_size; ++i) { @@ -76,14 +90,26 @@ class Array { } } - template - Array(const Arg&... args) { - static_assert( - sizeof...(args) == ct_size, - "Array::Array: number of arguments must be equal to ct_size" - ); + // ! Deprecated: variadic template constructor + // template + // Array(const Arg&... args) { + // static_assert( + // sizeof...(args) == ct_size, + // "Array::Array: number of arguments must be equal to ct_size" + // ); + // size_t index = 0; + // _construct_elements(index, args...); + // } + + Array(InitializerList il) { + if (il.size() != ct_size) { + throw std::runtime_error("Array::Array: number of arguments must be equal to ct_size"); + } + size_t index = 0; - _construct_elements(index, args...); + for (const auto& e : il) { + new (_data + index++) Element {e}; + } } Element& operator[](size_t index) { @@ -218,6 +244,20 @@ class ArrVec { using CtSize = Size; using CtCapacity = Size; + // STL compatible types + using value_type = Element; + // using allocator_type = std::allocator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = value_type*; + using const_iterator = const value_type*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + ArrVec() : _size {0} { // By definition none of the data in ArrVec of size 0 should be valid } @@ -286,14 +326,28 @@ class ArrVec { // template // ArrVec(const Arg&... args) : _data {args...}, _size(sizeof...(args)) {} - template - ArrVec(const Arg&... args) : _size(sizeof...(args)) { - static_assert( - sizeof...(args) <= ct_capacity, - "ArrVec::ArrVec: number of arguments must be less than or equal to ct_capacity" - ); + // ! Deprecated: variadic template constructor + // template + // ArrVec(const Arg&... args) : _size(sizeof...(args)) { + // static_assert( + // sizeof...(args) <= ct_capacity, + // "ArrVec::ArrVec: number of arguments must be less than or equal to ct_capacity" + // ); + // size_t index = 0; + // _construct_elements(index, args...); + // } + + ArrVec(InitializerList il) : _size(il.size()) { + if (il.size() > ct_capacity) { + throw std::runtime_error( + "ArrVec::ArrVec: number of arguments must be less than or equal to ct_capacity" + ); + } + size_t index = 0; - _construct_elements(index, args...); + for (const auto& e : il) { + new (_data + index++) Element {e}; + } } Element& operator[](size_t index) { @@ -520,20 +574,15 @@ namespace detail { // STL compatible types using value_type = Element; // using traits_type = Traits; - using allocator_type = Allocator; - using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - using iterator = - value_type*; // Simplification; actual implementation would be more complex - using const_iterator = const value_type*; // Simplification + using iterator = value_type*; + using const_iterator = const value_type*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -1361,7 +1410,7 @@ constexpr auto data(VectorView& as) -> const A* { return as.data(); } -#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 +// #if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 // template // auto operator<<(std::ostream& os, const A& seq) -> EnableIf< @@ -1382,7 +1431,7 @@ constexpr auto data(VectorView& as) -> const A* { // return os; // } -#endif +// #endif // Sequence trait implementation for std::array diff --git a/include/efp/string.hpp b/include/efp/string.hpp index 7f793f4..51ae464 100644 --- a/include/efp/string.hpp +++ b/include/efp/string.hpp @@ -351,7 +351,7 @@ using U8StringView = BasicStringView; // Use intercalate to join strings -#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 +// #if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 // template // auto operator<<(std::ostream& os, const A& seq) -> EnableIf< @@ -366,7 +366,7 @@ using U8StringView = BasicStringView; // return os; // } -#endif +// #endif }; // namespace efp diff --git a/include/efp/trait.hpp b/include/efp/trait.hpp index 41fce76..6fee8c0 100644 --- a/include/efp/trait.hpp +++ b/include/efp/trait.hpp @@ -110,7 +110,10 @@ struct IsSequence: False {}; template struct IsSequence, CtSize, CtCapacity>>: - All, IsSequenceImplNth, IsSequenceImplData> {}; + // All, IsSequenceImplNth, IsSequenceImplData> {}; + Bool<_all( + {IsSequenceImplLength::value, IsSequenceImplNth::value, IsSequenceImplData::value} + )> {}; // Utility type-level functions for Sequence trait diff --git a/test/efp/CMakeLists.txt b/test/efp/CMakeLists.txt index 3fa2f50..5740d78 100644 --- a/test/efp/CMakeLists.txt +++ b/test/efp/CMakeLists.txt @@ -22,5 +22,7 @@ target_link_libraries(efp_test PRIVATE Catch2::Catch2WithMain efp) +target_compile_features(efp_test PRIVATE cxx_std_11) -catch_discover_tests(efp_test) \ No newline at end of file +catch_discover_tests(efp_test) +efp_enable_compilation_time(efp_test) \ No newline at end of file diff --git a/test/efp/concurrency_test.hpp b/test/efp/concurrency_test.hpp index df29c08..0ceb7dd 100644 --- a/test/efp/concurrency_test.hpp +++ b/test/efp/concurrency_test.hpp @@ -1,8 +1,10 @@ -#include "catch2/catch_test_macros.hpp" +#ifndef CONCURRENCY_TEST_HPP_ +#define CONCURRENCY_TEST_HPP_ -#include "mth.hpp" +#include "efp/concurrency.hpp" +#include "test_common.hpp" -// todo Theading in Catch2 in unstable at the moment and aborting time to time. Though it passes if get run +using namespace efp; TEST_CASE("Enqueue and Dequeue", "[BlockingQ]") { BlockingQ queue; @@ -18,6 +20,15 @@ TEST_CASE("Enqueue and Dequeue", "[BlockingQ]") { CHECK(queue.dequeue() == 1); CHECK(queue.dequeue() == 2); } + + SECTION("Dequeue Order") { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + CHECK(queue.dequeue() == 1); + CHECK(queue.dequeue() == 2); + CHECK(queue.dequeue() == 3); + } } TEST_CASE("Enqueue and Dequeue Dynamic Capacity", "[BlockingQ]") { @@ -34,176 +45,87 @@ TEST_CASE("Enqueue and Dequeue Dynamic Capacity", "[BlockingQ]") { CHECK(queue.dequeue() == 1); CHECK(queue.dequeue() == 2); } -} - -// TEST_CASE("Blocking Behavior", "[BlockingQ]") { -// BlockingQ queue; - -// SECTION("Dequeue Blocks When Queue is Empty") { -// std::thread t([&]() { -// std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Delay to ensure dequeue blocks -// queue.enqueue(1); -// }); - -// auto start = std::chrono::high_resolution_clock::now(); -// CHECK(queue.dequeue() == 1); // This should block until the other thread enqueues -// auto end = std::chrono::high_resolution_clock::now(); -// auto duration = std::chrono::duration_cast(end - start); - -// CHECK(duration.count() >= 100); // Check if there was a block -// t.join(); -// } -// } - -// TEST_CASE("Blocking Behavior Dynamic Capacity", "[BlockingQ]") { -// BlockingQ queue; - -// SECTION("Dequeue Blocks When Queue is Empty") { -// std::thread t([&]() { -// std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Delay to ensure dequeue blocks -// queue.enqueue(1); -// }); - -// auto start = std::chrono::high_resolution_clock::now(); -// CHECK(queue.dequeue() == 1); // This should block until the other thread enqueues -// auto end = std::chrono::high_resolution_clock::now(); -// auto duration = std::chrono::duration_cast(end - start); - -// CHECK(duration.count() >= 100); // Check if there was a block -// t.join(); -// } -// } - -// TEST_CASE("Thread Safety", "[BlockingQ]") { -// BlockingQ queue; -// efp::Vector threads; - -// for (int i = 0; i < 10; ++i) { -// threads.emplace_back([&queue, i]() { -// queue.enqueue(i); -// }); -// } - -// efp::Vector results; -// for (int i = 0; i < 10; ++i) { -// threads.emplace_back([&queue, &results]() { -// results.push_back(queue.dequeue()); -// }); -// } - -// for (auto& t : threads) { -// t.join(); -// } - -// CHECK(results.size() == 10); // Ensure we dequeued 10 items -// // Further checks can be added to verify the dequeued values -// } - -// TEST_CASE("Thread Safety Dynamic Capacity", "[BlockingQ]") { -// BlockingQ queue; -// efp::Vector threads; - -// for (int i = 0; i < 10; ++i) { -// threads.emplace_back([&queue, i]() { -// queue.enqueue(i); -// }); -// } - -// efp::Vector results; -// for (int i = 0; i < 10; ++i) { -// threads.emplace_back([&queue, &results]() { -// results.push_back(queue.dequeue()); -// }); -// } - -// for (auto& t : threads) { -// t.join(); -// } - -// CHECK(results.size() == 10); // Ensure we dequeued 10 items -// // Further checks can be added to verify the dequeued values -// } - -TEST_CASE("Enqueue and Dequeue", "[NonBlockingQ]") { - NonBlockingQ queue; - - SECTION("Enqueue then Dequeue") { - queue.enqueue(1); - auto result = queue.dequeue(); - REQUIRE(result.has_value()); - REQUIRE(result.value() == 1); - } - - SECTION("Dequeue from Empty Queue") { - auto result = queue.dequeue(); - REQUIRE_FALSE(result.has_value()); - } - SECTION("Multiple Enqueue and Dequeue") { + SECTION("Dequeue Order") { queue.enqueue(1); queue.enqueue(2); - REQUIRE(queue.dequeue().value() == 1); - REQUIRE(queue.dequeue().value() == 2); + queue.enqueue(3); + CHECK(queue.dequeue() == 1); + CHECK(queue.dequeue() == 2); + CHECK(queue.dequeue() == 3); } } -TEST_CASE("Enqueue and Dequeue Dynamic Capacity", "[NonBlockingQ]") { +TEST_CASE("NonBlockingQ Operations", "[NonBlockingQ]") { NonBlockingQ queue; - SECTION("Enqueue then Dequeue") { - queue.enqueue(1); - auto result = queue.dequeue(); - REQUIRE(result.has_value()); - REQUIRE(result.value() == 1); + SECTION("Queue starts empty") { + CHECK(queue.is_empty()); } - SECTION("Dequeue from Empty Queue") { - auto result = queue.dequeue(); - REQUIRE_FALSE(result.has_value()); + SECTION("Enqueue adds elements") { + queue.enqueue(1); + CHECK_FALSE(queue.is_empty()); } - SECTION("Multiple Enqueue and Dequeue") { + SECTION("Dequeue returns elements in FIFO order") { queue.enqueue(1); queue.enqueue(2); - REQUIRE(queue.dequeue().value() == 1); - REQUIRE(queue.dequeue().value() == 2); - } -} - -TEST_CASE("Empty State", "[NonBlockingQ]") { - NonBlockingQ queue; + auto maybeOne = queue.dequeue(); + CHECK(maybeOne.has_value()); + CHECK(maybeOne.value() == 1); - SECTION("Initially Empty") { - REQUIRE(queue.is_empty()); + auto maybeTwo = queue.dequeue(); + CHECK(maybeTwo.has_value()); + CHECK(maybeTwo.value() == 2); } - SECTION("NotEmpty After Enqueue") { - queue.enqueue(1); - REQUIRE_FALSE(queue.is_empty()); + SECTION("Dequeue on empty queue returns efp::Nothing") { + auto result = queue.dequeue(); + CHECK_FALSE(result.has_value()); } - SECTION("Empty After Dequeuing Last Element") { - queue.enqueue(1); - queue.dequeue(); - REQUIRE(queue.is_empty()); + SECTION("Enqueue and dequeue multiple elements") { + for (int i = 1; i <= 5; ++i) { + queue.enqueue(i); + } + + for (int i = 1; i <= 5; ++i) { + auto result = queue.dequeue(); + CHECK(result.has_value()); + CHECK(result.value() == i); + } } } -TEST_CASE("Empty State Dynamic Capacity", "[NonBlockingQ]") { - NonBlockingQ queue; +TEST_CASE("DoubleBuffer Operations", "[DoubleBuffer]") { + DoubleBuffer buffer; // Assuming a buffer size of 10 for testing - SECTION("Initially Empty") { - REQUIRE(queue.is_empty()); + SECTION("Initial State is Empty") { + CHECK(buffer.empty()); } - SECTION("NotEmpty After Enqueue") { - queue.enqueue(1); - REQUIRE_FALSE(queue.is_empty()); + SECTION("Enqueue Adds Elements") { + buffer.enqueue(1); + buffer.swap_buffer(); // Swap to make the enqueued element available for dequeue + CHECK_FALSE(buffer.empty()); } - SECTION("Empty After Dequeuing Last Element") { - queue.enqueue(1); - queue.dequeue(); - REQUIRE(queue.is_empty()); + SECTION("Dequeue Removes Elements") { + buffer.enqueue(1); + buffer.swap_buffer(); + CHECK(buffer.dequeue() == 1); + CHECK(buffer.empty()); } -} \ No newline at end of file + + SECTION("Elements Move to Read Buffer on Swap") { + buffer.enqueue(1); + buffer.enqueue(2); + buffer.swap_buffer(); // Now, the elements should be in the read buffer + CHECK(buffer.dequeue() == 1); + CHECK(buffer.dequeue() == 2); + CHECK(buffer.empty()); + } +} + +#endif // CONCURRENCY_TEST_HPP_ \ No newline at end of file diff --git a/test/efp/cyclic_test copy.hpp b/test/efp/cyclic_test copy.hpp new file mode 100644 index 0000000..0d0bf96 --- /dev/null +++ b/test/efp/cyclic_test copy.hpp @@ -0,0 +1,332 @@ +#ifndef CYCLIC_TEST_HPP_ +#define CYCLIC_TEST_HPP_ + +#include "efp.hpp" + + +using namespace efp; + +TEST_CASE("Vcb Rule of 5", "Vcb") { + // use MockHW and MockRaii to check if the rule of 5 is followed + SECTION("New Construction", "Vcb") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcb vcb; + CHECK(MockHW::remaining_resource_count() == 6); + // CHECK(MockHW::resource_state_to_string() == "1, 2, 3, 4, 5, 6"); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Copy Construction", "Vcb") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcb vcb; + CHECK(MockHW::remaining_resource_count() == 6); + CHECK(MockHW::resource_state_to_int() == 123456); + + Vcb vcb_copy = vcb; + CHECK(MockHW::remaining_resource_count() == 12); + // CHECK(MockHW::resource_state_to_string() == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12"); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Copy Assignment", "Vcb") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcb vcb; + CHECK(MockHW::remaining_resource_count() == 6); + + Vcb vcb_copy; + CHECK(MockHW::remaining_resource_count() == 12); + + vcb_copy = vcb; + CHECK(MockHW::remaining_resource_count() == 12); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Move Construction", "Vcb") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcb vcb; + CHECK(MockHW::remaining_resource_count() == 6); + + Vcb vcb_move = efp::move(vcb); + CHECK(MockHW::remaining_resource_count() == 6); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Move Assignment", "Vcb") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcb vcb; + CHECK(MockHW::remaining_resource_count() == 6); + + Vcb vcb_move; + CHECK(MockHW::remaining_resource_count() == 12); + + vcb_move = efp::move(vcb); + CHECK(MockHW::remaining_resource_count() == 6); + } + CHECK(MockHW::is_sound()); + } + } +} + +TEST_CASE("Vcb") { + SECTION("0") { + Vcb vcb; + CHECK(IsSame, int>::value == true); + CHECK(vcb.size() == 3); + CHECK(length(vcb) == 3); + CHECK(IsCtConst::value == true); + // DebugType{}; + CHECK(vcb.empty() == false); + + vcb.push_back(1); + vcb.push_back(2); + vcb.push_back(3); + vcb.push_back(4); + + CHECK(vcb[0] == 2); + CHECK(vcb[1] == 3); + CHECK(vcb[2] == 4); + } +} + +TEST_CASE("Vcq Rule of 5", "Vcq") { + // use MockHW and MockRaii to check if the rule of 5 is followed + SECTION("New Construction", "Vcq") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcq vcq; + CHECK(MockHW::remaining_resource_count() == 0); + // CHECK(MockHW::resource_state_to_string() == "1, 2, 3, 4, 5, 6"); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Copy Construction", "Vcq") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_copy = vcq; + } + CHECK(MockHW::is_sound()); + } + + SECTION("Trivially Copiable Full") { + // todo with int + } + SECTION("Non-Trivially Copiable Full") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + // CHECK(MockHW::resource_state_to_string() == ""); + vcq.push_back(MockRaii {}); + // CHECK(MockHW::resource_state_to_string() == ""); + vcq.push_back(MockRaii {}); + // CHECK(MockHW::resource_state_to_string() == ""); + vcq.push_back(MockRaii {}); + // CHECK(MockHW::resource_state_to_string() == ""); + + Vcq vcq_copy = vcq; + CHECK(vcq.size() == 3); + // CHECK(MockHW::resource_state_to_string() == ""); + } + CHECK(MockHW::is_sound()); + // CHECK(MockHW::resource_state_to_string() == ""); + } + } + + SECTION("Copy Assignment", "Vcq") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_copy; + + vcq_copy = vcq; + } + CHECK(MockHW::is_sound()); + } + + SECTION("Trivially Copiable Full") { + // todo with int + } + SECTION("Non-Trivially Copiable Full") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_copy; + + vcq_copy = vcq; + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Move Construction", "Vcq") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_move = efp::move(vcq); + } + CHECK(MockHW::is_sound()); + } + + SECTION("Trivially Copiable Full") { + // todo with int + } + SECTION("Non-Trivially Copiable Full") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_move = efp::move(vcq); + } + CHECK(MockHW::is_sound()); + } + } + + SECTION("Move Assignment", "Vcq") { + SECTION("Trivially Copiable") { + // todo with int + } + SECTION("Non-Trivially Copiable") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_move; + + vcq_move = efp::move(vcq); + } + CHECK(MockHW::is_sound()); + } + + SECTION("Trivially Copiable Full") { + // todo with int + } + SECTION("Non-Trivially Copiable Full") { + { + MockHW::reset(); + Vcq vcq; + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + vcq.push_back(MockRaii {}); + + Vcq vcq_move; + + vcq_move = efp::move(vcq); + } + CHECK(MockHW::is_sound()); + } + } +} + +TEST_CASE("Vcq") { + SECTION("Array") { + Vcq vcq; + CHECK(IsSame, int>::value == true); + CHECK(vcq.size() == 0); + CHECK(vcq.empty() == true); + CHECK(length(vcq) == 0); + CHECK(IsCtConst::value == false); + + vcq.push_back(1); + + CHECK(vcq.size() == 1); + CHECK(vcq.empty() == false); + CHECK(vcq.pop_front() == 1); + + vcq.push_back(2); + vcq.push_back(3); + vcq.push_back(4); + + CHECK(vcq.size() == 3); + CHECK(vcq.pop_front() == 2); + CHECK(vcq.pop_front() == 3); + CHECK(vcq.pop_front() == 4); + + vcq.push_back(5); + vcq.push_back(6); + vcq.push_back(7); + vcq.push_back(8); + + CHECK(vcq.size() == 3); + CHECK(vcq[0] == 6); + CHECK(vcq[1] == 7); + CHECK(vcq[2] == 8); + CHECK(vcq.pop_front() == 6); + CHECK(vcq.pop_front() == 7); + CHECK(vcq.pop_front() == 8); + } +} + +#endif \ No newline at end of file diff --git a/test/efp/enum_test.hpp b/test/efp/enum_test.hpp index b01f357..7ffaf89 100644 --- a/test/efp/enum_test.hpp +++ b/test/efp/enum_test.hpp @@ -22,7 +22,7 @@ TEST_CASE("Enum Rule of 5", "Enum") { SECTION("Non-trivially copyable") { { MockHW::reset(); - Enum a = MockRaii{}; + Enum a = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 1); } CHECK(MockHW::is_sound()); @@ -41,7 +41,7 @@ TEST_CASE("Enum Rule of 5", "Enum") { SECTION("Non-trivially copyable") { { MockHW::reset(); - Enum a = MockRaii{}; + Enum a = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 1); Enum b = a; @@ -64,10 +64,10 @@ TEST_CASE("Enum Rule of 5", "Enum") { SECTION("Non-trivially copyable") { { MockHW::reset(); - Enum a = MockRaii{}; + Enum a = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 1); - Enum b = MockRaii{}; + Enum b = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 12); b = a; @@ -90,7 +90,7 @@ TEST_CASE("Enum Rule of 5", "Enum") { SECTION("Non-trivially copyable") { { MockHW::reset(); - Enum a = MockRaii{}; + Enum a = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 1); Enum b = efp::move(a); @@ -113,10 +113,10 @@ TEST_CASE("Enum Rule of 5", "Enum") { SECTION("Non-trivially copyable") { { MockHW::reset(); - Enum a = MockRaii{}; + Enum a = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 1); - Enum b = MockRaii{}; + Enum b = MockRaii {}; CHECK(MockHW::resource_state_to_int() == 12); b = efp::move(a); @@ -151,22 +151,18 @@ TEST_CASE("WildCard") { SECTION("Void return") { bool wild_card_work = false; - const auto wc = [&]() { - wild_card_work = true; - }; + const auto wc = [&]() { wild_card_work = true; }; - const auto wrapped = WildCardWrapper{wc}; + const auto wrapped = detail::WildCardWrapper> {wc}; wrapped(42); CHECK(wild_card_work == true); } SECTION("Non-void return") { - const auto wc = [&]() { - return 42; - }; + const auto wc = [&]() { return 42; }; - const auto wrapped = WildCardWrapper{wc}; + const auto wrapped = detail::WildCardWrapper> {wc}; CHECK(wrapped(unit) == 42); } @@ -187,26 +183,15 @@ TEST_CASE("enum_type") { // CHECK(b.get() == 1); } -// template -// struct Cons; - -// struct Nil {}; - -// template -// using List = Enum>; - -// template -// struct Cons { -// A head; -// List tail; -// }; - +// ! Deprecated TEST_CASE("Enum Exteneded Constructor") { SECTION("Explicit constructor") { struct A { A(bool arg) : value(arg) {} + bool value; }; + struct B {}; using SomeEnum = Enum; @@ -217,7 +202,7 @@ TEST_CASE("Enum Exteneded Constructor") { CHECK(SomeEnum::IsUniquelyConstructible::value); int idx_0 = (int)SomeEnum(true).index(); - int idx_1 = (int)SomeEnum{A{true}}.index(); + int idx_1 = (int)SomeEnum {A {true}}.index(); CHECK(idx_0 == idx_1); @@ -248,14 +233,16 @@ TEST_CASE("Enum Exteneded Constructor") { SECTION("Nested Enum") { struct A {}; + struct B {}; using Enum0 = Enum; + struct C {}; using NestedEnum = Enum; - CHECK(NestedEnum{A{}}.index() == NestedEnum{Enum0{A{}}}.index()); + CHECK(NestedEnum {A {}}.index() == NestedEnum {Enum0 {A {}}}.index()); } // SECTION("Self referencing Enum") { @@ -264,8 +251,16 @@ TEST_CASE("Enum Exteneded Constructor") { // } } +// SECTION("Self referencing Enum") { +// List empty_list = Nil{}; +// List one_list = Cons{42, Nil{}}; +// } + struct TemplateBranch { - template int operator()(const A& a) { return a * 2; } + template + int operator()(const A& a) { + return a * 2; + } }; TEST_CASE("enum_match") { @@ -273,13 +268,7 @@ TEST_CASE("enum_match") { Enum a = 42; double b = 0.; - a.match( - [&](int x) { - b += x; - }, - [&](double x) { - b++; - }); + a.match([&](int x) { b += x; }, [&](double x) { b++; }); //! Non-exhaustive match will not compile @@ -294,13 +283,7 @@ TEST_CASE("enum_match") { Enum a = 42.; double b = 0.; - a.match( - [&](int x) { - b += x; - }, - [&](double x) { - b++; - }); + a.match([&](int x) { b += x; }, [&](double x) { b++; }); CHECK(b == 1.); } @@ -308,13 +291,7 @@ TEST_CASE("enum_match") { SECTION("non-void0") { Enum a = unit; - int b = a.match( - [&](Unit x) { - return -1; - }, - [&](int x) { - return 1; - }); + int b = a.match([&](Unit x) { return -1; }, [&](int x) { return 1; }); CHECK(b == -1); } @@ -322,13 +299,7 @@ TEST_CASE("enum_match") { SECTION("non-void1") { Enum a = 42; - int b = a.match( - [&](Unit x) { - return -1; - }, - [&](int x) { - return x; - }); + int b = a.match([&](Unit x) { return -1; }, [&](int x) { return x; }); CHECK(b == 42); } @@ -338,15 +309,10 @@ TEST_CASE("enum_match") { int b = 42; int c = a.match( - [&](bool x) { - return b * 0; - }, - [&](int x) { - return b * 1; - }, - [&](double x) { - return b * 2; - }); + [&](bool x) { return b * 0; }, + [&](int x) { return b * 1; }, + [&](double x) { return b * 2; } + ); CHECK(b == 42); } @@ -356,54 +322,40 @@ TEST_CASE("enum_match") { int b = 42; int c = a.match( - [&](bool x) { - return b * 0; - }, - [&](int x) { - return b * 1; - }, - [&](double x) { - return b * 2; - }, - [&](float x) { - return b * 3; - }, - [&](long x) { - return b * 4; - }); + [&](bool x) { return b * 0; }, + [&](int x) { return b * 1; }, + [&](double x) { return b * 2; }, + [&](float x) { return b * 3; }, + [&](long x) { return b * 4; } + ); CHECK(b == 42); } SECTION("non-void3") { struct InitState {}; + struct AState {}; + struct BState {}; + struct ErrorState {}; + struct TerminatedState {}; using MajorState = efp::Enum; - MajorState a{InitState{}}; + MajorState a {InitState {}}; int b = 42; int c = a.match( - [&](InitState x) { - return b * 0; - }, - [&](AState x) { - return b * 1; - }, - [&](BState x) { - return b * 2; - }, - [&](ErrorState x) { - return b * 3; - }, - [&](TerminatedState x) { - return b * 4; - }); + [&](InitState x) { return b * 0; }, + [&](AState x) { return b * 1; }, + [&](BState x) { return b * 2; }, + [&](ErrorState x) { return b * 3; }, + [&](TerminatedState x) { return b * 4; } + ); CHECK(b == 42); } @@ -412,12 +364,8 @@ TEST_CASE("enum_match") { Enum a = unit; double b = 0.; - a.match( - [&](int x) { - b += 1; - }, - [&]() { - }); + //? + a.match([&](int x) { b += 1; }, [&]() {}); CHECK(b == 0.); @@ -431,9 +379,7 @@ TEST_CASE("enum_match") { SECTION("wild_card1") { Enum a = unit; - CHECK(a.match([]() { - return 42; - }) == 42); + CHECK(a.match([]() { return 42; }) == 42); } SECTION("non-trivial0") { @@ -441,13 +387,7 @@ TEST_CASE("enum_match") { Enum a = std::string("Hello"); int b = 42; - int c = a.match( - [&](bool x) { - return b * 0; - }, - [&](std::string x) { - return 42; - }); + int c = a.match([&](bool x) { return b * 0; }, [&](std::string x) { return 42; }); CHECK(b == 42); } @@ -457,13 +397,7 @@ TEST_CASE("enum_match") { Enum a = std::string("Hello"); int b = 42; - int c = a.match( - [&](bool x) { - return b * 0; - }, - [&](const std::string& x) { - return 42; - }); + int c = a.match([&](bool x) { return b * 0; }, [&](const std::string& x) { return 42; }); CHECK(b == 42); } @@ -476,50 +410,50 @@ TEST_CASE("enum_match") { // } } -// A test function to be pointed to -int enum_test_function() { return 42; } +// // A test function to be pointed to +// int enum_test_function() { +// return 42; +// } -TEST_CASE("Function Pointer in Enum") { - SECTION("Storing and retrieving function pointer") { - // Create an Enum instance holding a function pointer - Enum my_enum_func_ptr = enum_test_function; +// TEST_CASE("Function Pointer in Enum") { +// // SECTION("Storing and retrieving function pointer") { +// // // Create an Enum instance holding a function pointer +// // Enum my_enum_func_ptr = enum_test_function; - // Retrieve the function pointer and store it in a variable - int (*retrieved_func_ptr)() = my_enum_func_ptr.get(); +// // // Retrieve the function pointer and store it in a variable +// // int (*retrieved_func_ptr)() = my_enum_func_ptr.get(); - // Test if the retrieved function pointer is equal to the original one - CHECK(retrieved_func_ptr == &enum_test_function); - } +// // // Test if the retrieved function pointer is equal to the original one +// // CHECK(retrieved_func_ptr == &enum_test_function); +// // } - SECTION("Storing and retrieving lambda") { - // ! For some reason this does not work with const qualifier - auto enum_test_lambda = [&]() { - return 84; - }; +// // SECTION("Storing and retrieving lambda") { +// // // ! For some reason this does not work with const qualifier +// // auto enum_test_lambda = [&]() { return 84; }; - // Create an Enum instance holding a lambda - Enum my_enum_lambda(enum_test_lambda); +// // // Create an Enum instance holding a lambda +// // Enum my_enum_lambda(enum_test_lambda); - // Retrieve the lambda and store it in a variable - auto retrieved_lambda = my_enum_lambda.get(); +// // // Retrieve the lambda and store it in a variable +// // auto retrieved_lambda = my_enum_lambda.get(); - // Test if the retrieved lambda is equal to the original one - // This check confirms that the lambda is stored and retrieved correctly. - // Note: Direct lambda comparison might not be feasible; instead, test the behavior. - CHECK(retrieved_lambda() == enum_test_lambda()); - } +// // // Test if the retrieved lambda is equal to the original one +// // // This check confirms that the lambda is stored and retrieved correctly. +// // // Note: Direct lambda comparison might not be feasible; instead, test the behavior. +// // CHECK(retrieved_lambda() == enum_test_lambda()); +// // } - SECTION("Using function name directly") { - // Create an Enum instance using the function name directly - Enum my_enum_func_name = enum_test_function; +// // SECTION("Using function name directly") { +// // // Create an Enum instance using the function name directly +// // Enum my_enum_func_name = enum_test_function; - // Retrieve the function pointer and store it in a variable - int (*retrieved_func_name)() = my_enum_func_name.get(); +// // // Retrieve the function pointer and store it in a variable +// // int (*retrieved_func_name)() = my_enum_func_name.get(); - // Test if the retrieved function pointer is equal to the original one - CHECK(retrieved_func_name == &enum_test_function); - } -} +// // // Test if the retrieved function pointer is equal to the original one +// // CHECK(retrieved_func_name == &enum_test_function); +// // } +// } TEST_CASE("EnumAt") { CHECK(IsSame>, int>::value); diff --git a/test/efp/meta_test.hpp b/test/efp/meta_test.hpp index 206e865..2732371 100644 --- a/test/efp/meta_test.hpp +++ b/test/efp/meta_test.hpp @@ -18,36 +18,68 @@ using namespace efp; // // CHECK((-1 > CtConst{}) ? (CtConst{}) : ((-1 < 0) ? 0 : -1) == 0); // } +// TEST_CASE("_all") { +// CHECK(_all() == true); +// CHECK(_all(true) == true); +// CHECK(_all(false) == false); +// CHECK(_all(true, false) == false); +// CHECK(_all(false, true) == false); +// } + TEST_CASE("_all") { - CHECK(_all() == true); - CHECK(_all(true) == true); - CHECK(_all(false) == false); - CHECK(_all(true, false) == false); - CHECK(_all(false, true) == false); + // CHECK(_all({}) == true); + CHECK(_all({true}) == true); + CHECK(_all({false}) == false); + CHECK(_all({true, false}) == false); + CHECK(_all({false, true}) == false); } -TEST_CASE("any_v") { - CHECK(_any() == false); - CHECK(_any(true) == true); - CHECK(_any(false) == false); - CHECK(_any(true, false) == true); - CHECK(_any(false, true) == true); +// TEST_CASE("any_v") { +// CHECK(_any() == false); +// CHECK(_any(true) == true); +// CHECK(_any(false) == false); +// CHECK(_any(true, false) == true); +// CHECK(_any(false, true) == true); +// } + +TEST_CASE("_any") { + // CHECK(_any({}) == false); + CHECK(_any({true}) == true); + CHECK(_any({false}) == false); + CHECK(_any({true, false}) == true); + CHECK(_any({false, true}) == true); } +// TEST_CASE("_maximum") { +// CHECK(_maximum(0) == 0); +// CHECK(_maximum(0, 1) == 1); +// CHECK(_maximum(-1, 1) == 1); +// CHECK(_maximum(1., 2., 3) == 3.); +// CHECK(_maximum(-1., 2, 3.) == 3.); +// } + TEST_CASE("_maximum") { - CHECK(_maximum(0) == 0); - CHECK(_maximum(0, 1) == 1); - CHECK(_maximum(-1, 1) == 1); - CHECK(_maximum(1., 2., 3) == 3.); - CHECK(_maximum(-1., 2, 3.) == 3.); + CHECK(_maximum({0}) == 0); + CHECK(_maximum({0, 1}) == 1); + CHECK(_maximum({-1, 1}) == 1); + CHECK(_maximum({1., 2., 3.}) == 3.); + CHECK(_maximum({-1., 2., 3.}) == 3.); } +// TEST_CASE("_minimum") { +// CHECK(_minimum(0) == 0); +// CHECK(_minimum(1, 0) == 0); +// CHECK(_minimum(1u, -1) == 1); +// CHECK(_minimum(1, 2., 3) == 1); +// CHECK(_minimum(1, 2, -3.) == -3); +// } + TEST_CASE("_minimum") { - CHECK(_minimum(0) == 0); - CHECK(_minimum(1, 0) == 0); - CHECK(_minimum(1u, -1) == 1); - CHECK(_minimum(1, 2., 3) == 1); - CHECK(_minimum(1, 2, -3.) == -3); + CHECK(_minimum({0}) == 0); + CHECK(_minimum({1, 0}) == 0); + CHECK(_minimum({1, -1}) == -1); + CHECK(_minimum({1, 2, 3}) == 1); + CHECK(_minimum({1., 2., -3.}) == -3.); } TEST_CASE("IsSame") { diff --git a/test/efp/test.cpp b/test/efp/test.cpp index 3598275..47c1e43 100644 --- a/test/efp/test.cpp +++ b/test/efp/test.cpp @@ -10,3 +10,4 @@ #include "./string_test.hpp" #include "./sort_test.hpp" #include "./format_test.hpp" +#include "./concurrency_test.hpp"