diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b1a060d..55f1f0ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ # by David L. Dight # see https://github.com/fix8mt/conjure_enum # -# Lightweight header-only C++20 enum and type reflection +# Lightweight header-only C++20 enum and typename reflection # # Licensed under the MIT License . # @@ -32,7 +32,7 @@ cmake_minimum_required (VERSION 3.20) project (conjure_enum LANGUAGES CXX HOMEPAGE_URL https://github.com/fix8mt/conjure_enum - DESCRIPTION "Lightweight C++20 enum and type reflection" + DESCRIPTION "Lightweight C++20 enum and typename reflection" VERSION 1.0.0) # to disable strip: @@ -52,7 +52,9 @@ message("-- Build unit tests: ${BUILD_UNITTESTS}") function(build loc x) add_executable(${x} ${loc}/${x}.cpp) - add_dependencies(${x} srcloctest) # srcloctest should be built even if errors with conjure_enum + if(NOT ${x} STREQUAL srcloctest) + add_dependencies(${x} srcloctest) # srcloctest should be built even if errors with conjure_enum + endif() set_target_properties(${x} PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED true) target_include_directories(${x} PRIVATE include) if(BUILD_ALL_WARNINGS) diff --git a/README.md b/README.md index d5a03e91..27ffe111 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // @@ -32,7 +32,7 @@

-

Lightweight header-only C++20 enum and type reflection

+

Lightweight header-only C++20 enum and typename reflection

--- @@ -64,7 +64,7 @@ Based on the awesome work in [`magic_enum`](https://github.com/Neargye/magic_enum)[^2] and [`boost::describe`](https://github.com/boostorg/describe), this library offers a streamlined and powerful way to add reflection capabilities to your C++ enums and other types. We've optimized the core functionality, focusing on the main features developers usually want while enhancing and expanding them for a more efficient and expressive experience. We've also -added general purpose type reflection for any type. +added general purpose typename reflection for any type. ## b) Embrace the Future with C++20 @@ -97,6 +97,7 @@ unlocked the potential of `constexpr` algorithms and concepts. This translates t - `add_scope` - `remove_scope` - `unscoped_string_to_enum` + - `for_each_n` - iterators and more! - ***Transparency***: Compiler implementation variability fully documented, verifiable and reportable (see 9 above) @@ -414,7 +415,7 @@ _output_ true false ``` -## n) `for_each` +## n) `for_each`, `for_each_n` ```c++ template requires std::invocable @@ -423,9 +424,18 @@ requires std::invocable template // specialisation for member function with object requires std::invocable [[maybe_unused]] static constexpr auto for_each(Fn&& func, C *obj, Args&&... args); + +template +requires std::invocable +[[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, Args&&... args); + +template // specialisation for member function with object +requires std::invocable +[[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args); ``` Call supplied invocable for _each_ enum value. Similar to `std::for_each` except the first parameter of your invocable must accept an enum value (passed by `for_each`). -Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. The second version is intended to be used +Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. You can limit the number of calls to your +invokable by using the `for_each_n` version with the first parameter being the maximum number to call. The second version of `for_each` and `for_each_n` is intended to be used when using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. @@ -452,6 +462,19 @@ _output_ 13 10 14 10 ``` +Above example using `for_each_n`, limiting to 3: +```c++ +conjure_enum::for_each_n(3, [](component val, int other) +{ + std::cout << static_cast(val) << ' ' << other << '\n'; +}, 10); +``` +_output_ +```CSV +0 10 +1 10 +2 10 +``` Example using returned object and additional reference parameter: ```c++ int total{}; @@ -708,7 +731,7 @@ constexpr enum_bitset(U bits); constexpr enum_bitset(std::string_view from, bool anyscope=false, char sep='|', bool ignore_errors=true); -template +template constexpr enum_bitset(E... comp); template @@ -811,7 +834,7 @@ All of the standard accessors and mutators are supported. | Method | Description | | :--- | :--- | | `test` | test for bit(s)| -| `set` | set bit(s)| +| `set` | set all or 1 bit, optionally set to off| | `reset` | reset bits(s)| | `flip` | flip bits(s)| | `to_ulong` | convert to `unsigned long` | @@ -819,26 +842,33 @@ All of the standard accessors and mutators are supported. | `count` | count of bits on | | `size` | number of bits in bitset | | `operator[]` | test bit at position | +| `any` | return true if any bit is on | +| `all` | return true if all bits are on | +| `none` | return true if no bits are on | Additional methods | Method | Description | | :--- | :--- | -| `set_all` | set all specified bits | -| `reset_all` | reset all specified bits | -| `test_any` | test for one or more bits | -| `test_all` | test for all specified bits | +| `set` | set all specified bits, templated | +| `reset` | reset all specified bits, templated | +| `any_of` | test for one or more bits, templated, function, types and underlyings | +| `all_of` | test for all specified bits, templated, function, types and underlyings | +| `none_of` | test for all specified bits set to off, templated, function, types and underlyings | +| `not_count` | complement of count, count of off bits | + +Take a look at the [implementation](include/fix8/conjure_enum.hpp) for more detail on the various API functions available. All accessors and mutators work with enum values or integers as with operators. They also work with multiple values, either as template parameters or as variadic arguments: ```c++ enum_bitset eb; -eb.set_all(); +eb.set(); std::cout << eb << '\n'; -std::cout << std::boolalpha << eb.test_all() << '\n'; +std::cout << std::boolalpha << eb.all_of() << '\n'; eb.reset(); -std::cout << std::boolalpha << eb.test_all(0, 2, 5, 9) << '\n'; -std::cout << std::boolalpha << eb.test_any(0, 2, 5, 9) << '\n'; -std::cout << std::boolalpha << eb.test_all(numbers::zero,numbers::nine) << '\n'; +std::cout << std::boolalpha << eb.all_of(0, 2, 5, 9) << '\n'; +std::cout << std::boolalpha << eb.any_of(0, 2, 5, 9) << '\n'; +std::cout << std::boolalpha << eb.all_of(numbers::zero,numbers::nine) << '\n'; std::cout << eb << '\n'; eb.reset(numbers::nine) std::cout << ec << '\n'; @@ -887,7 +917,7 @@ _output_ 0001001010 ---+--+-+- ``` -### iii. `for_each` +### iii. `for_each`, `for_each_n` ```c++ template requires std::invocable @@ -896,9 +926,18 @@ requires std::invocable template // specialisation for member function with object requires std::invocable [[maybe_unused]] constexpr auto for_each(Fn&& func, C *obj, Args&&... args); + +template +requires std::invocable +[[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, Args&&... args); + +template // specialisation for member function with object +requires std::invocable +[[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args); ``` Call supplied invocable for _each bit that is on_. Similar to `std::for_each` except first parameter of your invocable must accept an enum value (passed by `for_each`). -Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. The second version is intended to be used +Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. You can limit the number of calls to your +invokable by using the `for_each_n` version with the first parameter being the maximum number to call. The second version of `for_each` and `for_each_n` is intended to be used when using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. @@ -927,10 +966,20 @@ numbers::two numbers::five numbers::nine ``` +Above example using `for_each_n`, limiting to 3: +```c++ +ec.for_each_n(3, &foo::printer, &bar, std::ref(std::cout)); +``` +_output_ +```CSV +numbers::zero +numbers::two +numbers::five +``` --- # 5. API and Examples using `conjure_type` -`conjure_type` is a general purpose class allowing you to extract a string representation of any type. +`conjure_type` is a general purpose class allowing you to extract a string representation of any typename. The string will be stored statically by the compiler, so you can use the statically generated value `name` to obtain your type. ```c++ template @@ -1119,6 +1168,10 @@ static const char *conjure_type::tpeek() [T = TEST::TES $ ``` +## e) Contributing +Contributions are welcome. Make your changes in [your fork on the dev branch](https://github.com/fix8mt/conjure_enum/tree/dev) and open a pull request from there. PRs to +master will not be considered. + --- # 7. Notes ## a) enum limits diff --git a/SECURITY.md b/SECURITY.md index 2d7888ea..c0816e9f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // @@ -30,18 +30,18 @@ //----------------------------------------------------------------------------------------> # Security Policy Security Policy -This document outlines the security policy for the conjure_enum project. Here, we detail how to report vulnerabilities and how we handle them. +This document outlines the security policy for the `conjure_enum` project. Here, we detail how to report vulnerabilities and how we handle them. ## Reporting a Vulnerability -If you discover a potential security vulnerability in conjure_enum, we encourage you to report it responsibly. Here's how: +If you discover a potential security vulnerability in `conjure_enum`, we encourage you to report it responsibly. Here's how: - **Privately Contact Us:** Please file a [detailed report](https://github.com/fix8mt/conjure_enum/security/advisories/new). - **Include Details:** In your email, please provide the following information (if applicable): - A clear description of the vulnerability. - Steps to reproduce the vulnerability (if possible). - The potential impact of the vulnerability. -We appreciate your cooperation in keeping conjure_enum secure. We will work with you confidentially to address the vulnerability as quickly as possible. +We appreciate your cooperation in keeping `conjure_enum` secure. We will work with you confidentially to address the vulnerability as quickly as possible. ## Disclosure Process diff --git a/examples/example.cpp b/examples/example.cpp index f091990c..9cf0ed2b 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // @@ -73,6 +73,7 @@ const std::string demangle() noexcept int main(void) { + conjure_enum::for_each_n(3, [](component val, int other) { std::cout << static_cast(val) << ' ' << other << '\n'; }, 200); int total{}; auto myfunc { conjure_enum::for_each([](component val, int other, int& tot) { @@ -83,13 +84,13 @@ int main(void) std::cout << total << '\n'; enum_bitset eb; - eb.set_all(); + eb.set(); std::cout << eb << '\n'; - std::cout << std::boolalpha << eb.test_all() << '\n'; - eb.reset_all(); - std::cout << std::boolalpha << eb.test_all(0, 2, 5, 9) << '\n'; - std::cout << std::boolalpha << eb.test_any(0, 2, 5, 9) << '\n'; - std::cout << std::boolalpha << eb.test_all(numbers::zero,numbers::nine) << '\n'; + std::cout << std::boolalpha << eb.all_of() << '\n'; + eb.reset(); + std::cout << std::boolalpha << eb.all_of(0, 2, 5, 9) << '\n'; + std::cout << std::boolalpha << eb.any_of(0, 2, 5, 9) << '\n'; + std::cout << std::boolalpha << eb.all_of(numbers::zero,numbers::nine) << '\n'; std::cout << eb << '\n'; eb.reset(numbers::nine); std::cout << eb << '\n'; @@ -195,6 +196,7 @@ int main(void) } }; const foo bar; + conjure_enum::for_each_n(3, &foo::printer, &bar, 1000); ek.for_each(std::bind(&foo::printer, &bar, std::placeholders::_1, 10)); ek.for_each(&foo::printer, &bar, 10); [[maybe_unused]] enum_bitset er("one|three|four|eight"sv, true); diff --git a/examples/srcloctest.cpp b/examples/srcloctest.cpp index df63f14c..a72a5196 100644 --- a/examples/srcloctest.cpp +++ b/examples/srcloctest.cpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // diff --git a/examples/statictest.cpp b/examples/statictest.cpp index f8165ee4..367af156 100644 --- a/examples/statictest.cpp +++ b/examples/statictest.cpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // diff --git a/include/fix8/conjure_enum.hpp b/include/fix8/conjure_enum.hpp index 10240cfd..87c23158 100644 --- a/include/fix8/conjure_enum.hpp +++ b/include/fix8/conjure_enum.hpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Parts based on magic_enum // Copyright (c) 2019 - 2024 Daniil Goncharov . @@ -179,7 +179,7 @@ class conjure_enum static constexpr auto _unscoped_entries(std::index_sequence) noexcept { std::array tmp{{{ values[I], _remove_scope(_enum_name_v)}...}}; - std::sort(tmp.begin(), tmp.end(), tuple_comp_rev); + std::sort(tmp.begin(), tmp.end(), _tuple_comp_rev); return tmp; } @@ -194,7 +194,7 @@ class conjure_enum static constexpr auto _scoped_entries(std::index_sequence) noexcept { std::array tmp{{{ _remove_scope(_enum_name_v), _enum_name_v}...}}; - std::sort(tmp.begin(), tmp.end(), scoped_comp); + std::sort(tmp.begin(), tmp.end(), _scoped_comp); return tmp; } @@ -202,14 +202,14 @@ class conjure_enum static constexpr auto _rev_scoped_entries(std::index_sequence) noexcept { std::array tmp{{{ _enum_name_v, _remove_scope(_enum_name_v)}...}}; - std::sort(tmp.begin(), tmp.end(), scoped_comp); + std::sort(tmp.begin(), tmp.end(), _scoped_comp); return tmp; } static constexpr auto _sorted_entries() noexcept { auto tmp { entries }; - std::sort(tmp.begin(), tmp.end(), tuple_comp_rev); + std::sort(tmp.begin(), tmp.end(), _tuple_comp_rev); return tmp; } @@ -296,19 +296,30 @@ class conjure_enum return {}; } - static constexpr bool value_comp(const T& pl, const T& pr) noexcept + static constexpr std::string_view _process_scope([[maybe_unused]] const auto& entr, std::string_view what) noexcept + { + if constexpr (is_scoped()) + if (const auto result { std::equal_range(entr.cbegin(), + entr.cend(), scoped_tuple(what, std::string_view()), _scoped_comp) }; + result.first != result.second) + return std::get<1>(*result.first); + return what; + } + + /// comparators + static constexpr bool _value_comp(const T& pl, const T& pr) noexcept { return static_cast(pl) < static_cast(pr); } - static constexpr bool tuple_comp(const enum_tuple& pl, const enum_tuple& pr) noexcept + static constexpr bool _tuple_comp(const enum_tuple& pl, const enum_tuple& pr) noexcept { return static_cast(std::get(pl)) < static_cast(std::get(pr)); } - static constexpr bool tuple_comp_rev(const enum_tuple& pl, const enum_tuple& pr) noexcept + static constexpr bool _tuple_comp_rev(const enum_tuple& pl, const enum_tuple& pr) noexcept { return std::get(pl) < std::get(pr); } - static constexpr bool scoped_comp(const scoped_tuple& pl, const scoped_tuple& pr) noexcept + static constexpr bool _scoped_comp(const scoped_tuple& pl, const scoped_tuple& pr) noexcept { return std::get<0>(pl) < std::get<0>(pr); } @@ -361,22 +372,12 @@ class conjure_enum static constexpr std::string_view remove_scope(std::string_view what) noexcept { - if constexpr (is_scoped()) - if (const auto result { std::equal_range(rev_scoped_entries.cbegin(), - rev_scoped_entries.cend(), scoped_tuple(what, std::string_view()), scoped_comp) }; - result.first != result.second) - return std::get<1>(*result.first); - return what; + return _process_scope(rev_scoped_entries, what); } static constexpr std::string_view add_scope(std::string_view what) noexcept { - if constexpr (is_scoped()) - if (const auto result { std::equal_range(scoped_entries.cbegin(), - scoped_entries.cend(), scoped_tuple(what, std::string_view()), scoped_comp) }; - result.first != result.second) - return std::get<1>(*result.first); - return what; + return _process_scope(scoped_entries, what); } static constexpr bool has_scope(std::string_view what) noexcept @@ -404,60 +405,79 @@ class conjure_enum } static constexpr std::optional int_to_enum(int value) noexcept { - if (const auto result { std::equal_range(values.cbegin(), values.cend(), static_cast(value), value_comp) }; + if (const auto result { std::equal_range(values.cbegin(), values.cend(), static_cast(value), _value_comp) }; result.first != result.second) return *result.first; return {}; } static constexpr bool contains(T value) noexcept { - const auto result { std::equal_range(values.cbegin(), values.cend(), value, value_comp) }; + const auto result { std::equal_range(values.cbegin(), values.cend(), value, _value_comp) }; return result.first != result.second; } static constexpr bool contains(std::string_view str) noexcept { - const auto result { std::equal_range(sorted_entries.cbegin(), sorted_entries.cend(), enum_tuple(T{}, str), tuple_comp_rev) }; + const auto result { std::equal_range(sorted_entries.cbegin(), sorted_entries.cend(), enum_tuple(T{}, str), _tuple_comp_rev) }; return result.first != result.second; } static constexpr std::string_view enum_to_string(T value, bool noscope=false) noexcept { - if (const auto result { std::equal_range(entries.cbegin(), entries.cend(), enum_tuple(value, std::string_view()), tuple_comp) }; + if (const auto result { std::equal_range(entries.cbegin(), entries.cend(), enum_tuple(value, std::string_view()), _tuple_comp) }; result.first != result.second) return noscope ? remove_scope(std::get(*result.first)) : std::get(*result.first); return {}; } static constexpr std::optional string_to_enum(std::string_view str) noexcept { - if (const auto result { std::equal_range(sorted_entries.cbegin(), sorted_entries.cend(), enum_tuple(T{}, str), tuple_comp_rev) }; + if (const auto result { std::equal_range(sorted_entries.cbegin(), sorted_entries.cend(), enum_tuple(T{}, str), _tuple_comp_rev) }; result.first != result.second) return std::get(*result.first); return {}; } static constexpr std::optional unscoped_string_to_enum(std::string_view str) noexcept { - if (const auto result { std::equal_range(unscoped_entries.cbegin(), unscoped_entries.cend(), enum_tuple(T{}, str), tuple_comp_rev) }; + if (const auto result { std::equal_range(unscoped_entries.cbegin(), unscoped_entries.cend(), enum_tuple(T{}, str), _tuple_comp_rev) }; result.first != result.second) return std::get(*result.first); return {}; } + /// for_each, for_each_n template requires std::invocable [[maybe_unused]] static constexpr auto for_each(Fn&& func, Args&&... args) noexcept { - for (const auto ev : values) - std::invoke(std::forward(func), ev, std::forward(args)...); - return std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...); + return for_each_n(static_cast(count()), std::forward(func), std::forward(args)...); } - // specialisation for member function with object - template + template // specialisation for member function with object requires std::invocable [[maybe_unused]] static constexpr auto for_each(Fn&& func, C *obj, Args&&... args) noexcept { return for_each(std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)); } + template + requires std::invocable + [[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, Args&&... args) noexcept + { + for (int ii{}; const auto ev : values) + { + if (ii++ < n) + std::invoke(std::forward(func), ev, std::forward(args)...); + else + break; + } + return std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...); + } + + template // specialisation for member function with object + requires std::invocable + [[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args) noexcept + { + return for_each_n(n, std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)); + } + // public constexpr data structures static constexpr auto values { _values() }; static constexpr auto entries { _entries(std::make_index_sequence()) }; @@ -498,7 +518,6 @@ namespace ostream_enum_operator template concept valid_bitset_enum = valid_enum and requires(T) { - requires conjure_enum::count() > 0; requires static_cast(conjure_enum::values.back()) < conjure_enum::count(); }; @@ -534,6 +553,7 @@ class enum_bitset constexpr std::size_t count() const noexcept { return std::popcount(static_cast>(_present)); } // C++23: upgrade to std::bitset when count is constexpr + constexpr std::size_t not_count() const noexcept { return countof - count(); } constexpr std::size_t size() const noexcept { return countof; } constexpr U to_ulong() const noexcept { return _present; } constexpr unsigned long long to_ullong() const noexcept { return _present; } @@ -541,34 +561,39 @@ class enum_bitset constexpr bool operator[](std::size_t pos) const noexcept { return test(pos); } constexpr bool operator[](T what) const noexcept { return test(what); } - constexpr void set(U pos) noexcept { _present |= (1 << pos); } - constexpr void set(T what) noexcept { set(to_underlying(what)); } + /// set + constexpr void set(U pos, bool value=true) noexcept { value ? _present |= 1 << pos : _present &= ~(1 << pos); } + constexpr void set(T what, bool value=true) noexcept { set(to_underlying(what), value); } constexpr void set() noexcept { _present = all_bits; } template constexpr void set() noexcept { if constexpr (constexpr auto uu{to_underlying()}; uu < countof) - _present |= (1 << uu); + _present |= 1 << uu; } template - constexpr void set_all() noexcept { (set(),...); } + requires (sizeof...(comp) > 1) + constexpr void set() noexcept { (set(),...); } template - constexpr void set_all(E... comp) noexcept { return (... | (set(comp))); } + requires (sizeof...(E) > 1) + constexpr void set(E... comp) noexcept { return (... | (set(comp))); } + /// flip template constexpr void flip() noexcept { if constexpr (constexpr auto uu{to_underlying()}; uu < countof) - _present ^= (1 << uu); + _present ^= 1 << uu; } constexpr void flip() noexcept { _present = ~_present & all_bits; } - constexpr void flip(U pos) noexcept { _present ^= (1 << pos); } + constexpr void flip(U pos) noexcept { _present ^= 1 << pos; } constexpr void flip(T what) noexcept { flip(to_underlying(what)); } + /// reset template constexpr void reset() noexcept { @@ -581,12 +606,14 @@ class enum_bitset constexpr void reset(T what) noexcept { reset(to_underlying(what)); } template - constexpr void reset_all() noexcept { (reset(),...); } + requires (sizeof...(comp) > 1) + constexpr void reset() noexcept { (reset(),...); } template - constexpr void reset_all(I...comp) noexcept { (reset(comp),...); } + constexpr void reset(I...comp) noexcept { (reset(comp),...); } - constexpr bool test(U pos) const noexcept { return _present & (1 << pos); } + /// test + constexpr bool test(U pos) const noexcept { return _present & 1 << pos; } constexpr bool test(T what) const noexcept { return test(to_underlying(what)); } constexpr bool test() const noexcept { return _present; } @@ -600,52 +627,63 @@ class enum_bitset } template - constexpr bool test_any() const noexcept { return (... || test()); } + constexpr bool any_of() const noexcept { return (... || test()); } template - constexpr bool test_any(I...comp) const noexcept { return (... || test(comp)); } + constexpr bool any_of(I...comp) const noexcept { return (... || test(comp)); } template - constexpr bool test_any(E...comp) const noexcept { return (... || test(comp)); } + constexpr bool any_of(E...comp) const noexcept { return (... || test(comp)); } template - constexpr bool test_all() const noexcept { return (... && test()); } + constexpr bool all_of() const noexcept { return (... && test()); } template - constexpr bool test_all(I...comp) const noexcept { return (... && test(comp)); } + constexpr bool all_of(I...comp) const noexcept { return (... && test(comp)); } template - constexpr bool test_all(E...comp) const noexcept { return (... && test(comp)); } + constexpr bool all_of(E...comp) const noexcept { return (... && test(comp)); } + template + constexpr bool none_of() const noexcept { return (... && !test()); } + + template + constexpr bool none_of(I...comp) const noexcept { return (... && !test(comp)); } + + template + constexpr bool none_of(E...comp) const noexcept { return (... && !test(comp)); } + + constexpr bool any() const noexcept { return count(); } + constexpr bool all() const noexcept { return _present == all_bits; } + constexpr bool none() const noexcept { return !*this; } + + /// operators + constexpr operator bool() const noexcept { return count(); } constexpr enum_bitset& operator<<=(std::size_t pos) noexcept { _present <<= pos; return *this; } constexpr enum_bitset& operator>>=(std::size_t pos) noexcept { _present >>= pos; return *this; } - constexpr enum_bitset& operator&=(T other) noexcept { _present &= (1 << to_underlying(other)); return *this; } - constexpr enum_bitset& operator|=(T other) noexcept { _present |= (1 << to_underlying(other)); return *this; } - constexpr enum_bitset& operator^=(T other) noexcept { _present ^= (1 << to_underlying(other)); return *this; } + constexpr enum_bitset& operator&=(T other) noexcept { _present &= 1 << to_underlying(other); return *this; } + constexpr enum_bitset& operator|=(T other) noexcept { _present |= 1 << to_underlying(other); return *this; } + constexpr enum_bitset& operator^=(T other) noexcept { _present ^= 1 << to_underlying(other); return *this; } constexpr enum_bitset& operator&=(U other) noexcept { _present &= other; return *this; } constexpr enum_bitset& operator|=(U other) noexcept { _present |= other; return *this; } constexpr enum_bitset& operator^=(U other) noexcept { _present ^= other; return *this; } constexpr enum_bitset operator<<(int pos) const noexcept { return enum_bitset(_present << pos); } constexpr enum_bitset operator>>(int pos) const noexcept { return enum_bitset(_present >> pos); } - constexpr enum_bitset operator&(T other) const noexcept { return enum_bitset(_present & (1 << to_underlying(other))); } - constexpr enum_bitset operator|(T other) const noexcept { return enum_bitset(_present | (1 << to_underlying(other))); } - constexpr enum_bitset operator^(T other) const noexcept { return enum_bitset(_present ^ (1 << to_underlying(other))); } + constexpr enum_bitset operator&(T other) const noexcept { return enum_bitset(_present & 1 << to_underlying(other)); } + constexpr enum_bitset operator|(T other) const noexcept { return enum_bitset(_present | 1 << to_underlying(other)); } + constexpr enum_bitset operator^(T other) const noexcept { return enum_bitset(_present ^ 1 << to_underlying(other)); } constexpr enum_bitset operator&(U other) const noexcept { return enum_bitset(_present & other); } constexpr enum_bitset operator|(U other) const noexcept { return enum_bitset(_present | other); } constexpr enum_bitset operator^(U other) const noexcept { return enum_bitset(_present ^ other); } constexpr enum_bitset operator~() const noexcept { return enum_bitset(~_present & all_bits); } - constexpr operator bool() const noexcept { return count(); } - + /// for_each, for_each_n template requires std::invocable [[maybe_unused]] constexpr auto for_each(Fn&& func, Args&&... args) noexcept { - for (const auto ev : conjure_enum::values) - if (test(ev)) - std::invoke(std::forward(func), ev, std::forward(args)...); - return std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...); + return for_each_n(static_cast(countof), std::forward(func), std::forward(args)...); } template // specialisation for member function with object @@ -655,6 +693,30 @@ class enum_bitset return for_each(std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)); } + template + requires std::invocable + [[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, Args&&... args) noexcept + { + for (int ii{}; const auto ev : conjure_enum::values) + { + if (test(ev)) + { + if (ii++ < n) + std::invoke(std::forward(func), ev, std::forward(args)...); + else + break; + } + } + return std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...); + } + + template // specialisation for member function with object + requires std::invocable + [[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args) noexcept + { + return for_each_n(n, std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)); + } + /// create a bitset from custom separated enum string static constexpr U factory(std::string_view src, bool anyscope, char sep, bool ignore_errors) { diff --git a/reference/source_location.md b/reference/source_location.md index 6aa9a2b0..34eb6ff9 100644 --- a/reference/source_location.md +++ b/reference/source_location.md @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // diff --git a/utests/edgetests.cpp b/utests/edgetests.cpp index c635ce50..479f72bc 100644 --- a/utests/edgetests.cpp +++ b/utests/edgetests.cpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // diff --git a/utests/unittests.cpp b/utests/unittests.cpp index ca3784b7..cc0eb188 100644 --- a/utests/unittests.cpp +++ b/utests/unittests.cpp @@ -7,7 +7,7 @@ // by David L. Dight // see https://github.com/fix8mt/conjure_enum // -// Lightweight header-only C++20 enum and type reflection +// Lightweight header-only C++20 enum and typename reflection // // Licensed under the MIT License . // @@ -326,14 +326,37 @@ TEST_CASE("for_each") REQUIRE(total == 160); } +//----------------------------------------------------------------------------------------- +TEST_CASE("for_each_n") +{ + int total{}; + conjure_enum::for_each_n(3, [](component val, int& tot) + { + tot += static_cast(val); + }, std::ref(total)); + REQUIRE(total == 3); + + struct foo + { + void process(component val, int offset, int& tot) + { + tot += offset + static_cast(val); + } + }; + foo bar; + total = 0; + conjure_enum::for_each_n(3, &foo::process, &bar, 10, std::ref(total)); + REQUIRE(total == 33); +} + //----------------------------------------------------------------------------------------- TEST_CASE("enum_bitset") { enum_bitset eb; - eb.set_all(); - REQUIRE(eb.test_all()); + eb.set(); + REQUIRE(eb.all_of()); eb.reset(); // use alias - REQUIRE(!eb.test_all()); + REQUIRE(!eb.all_of()); eb.reset(numbers::nine); REQUIRE(!eb.test(numbers::nine)); @@ -360,14 +383,15 @@ TEST_CASE("enum_bitset") REQUIRE(!ec.test()); ec.set(numbers::three); REQUIRE(ec.test()); + ec.set(numbers::three, false); + REQUIRE(ec.test() == false); + REQUIRE(ec.any()); } //----------------------------------------------------------------------------------------- TEST_CASE("enum_bitset ops") { enum_bitset ed(numbers::two,numbers::three,numbers::four,numbers::seven); - REQUIRE(ed.test_all()); - REQUIRE(ed.test_any()); REQUIRE((ed << 1).to_ulong() == 0b0100111000); ed <<= 1; REQUIRE(ed.to_ulong() == 0b0100111000); @@ -390,6 +414,24 @@ TEST_CASE("enum_bitset ops") REQUIRE(ed.to_ulong() == 0b010); } +//----------------------------------------------------------------------------------------- +TEST_CASE("enum_bitset ext ops") +{ + enum_bitset ed; + REQUIRE(ed.none()); + ed.set(); + REQUIRE(ed.all()); + enum_bitset ee(numbers::one,numbers::two,numbers::three,numbers::four,numbers::five); + REQUIRE(ee.all_of()); + REQUIRE(ee.any_of()); + int a{static_cast(numbers::two)}, b{static_cast(numbers::three)}, c{static_cast(numbers::four)}, d{static_cast(numbers::five)}; + REQUIRE(ee.any_of(a,b,c,d)); + ee.reset(); + REQUIRE(ee.none_of()); + REQUIRE(ee.all_of()); + REQUIRE(ee.not_count() == 10 - 2); +} + //----------------------------------------------------------------------------------------- TEST_CASE("enum_bitset(std::string_view)") { @@ -424,3 +466,27 @@ numbers::seven(7) REQUIRE(total == 16); } +//----------------------------------------------------------------------------------------- +TEST_CASE("enum_bitset::for_each_n") +{ + enum_bitset ee(0b10101010); + std::ostringstream ostr; + ee.for_each_n(3, [&ostr](numbers val) noexcept + { + ostr << conjure_enum::enum_to_string(val) << '(' << static_cast(val) << ')' << '\n'; + }); + REQUIRE(ostr.str() == +R"(numbers::one(1) +numbers::three(3) +numbers::five(5) +)"); + + int total{}; + enum_bitset enc(numbers::two,numbers::three,numbers::four,numbers::seven); + enc.for_each_n(3, [](numbers val, int& tot) + { + tot += static_cast(val); + }, std::ref(total)); + REQUIRE(total == 9); +} +