diff --git a/README.md b/README.md index b85ef434..83a40b72 100644 --- a/README.md +++ b/README.md @@ -500,7 +500,8 @@ _output_ 14 10 14 10 <== invoked with returned object 74 -```Example with pointer to member function with additional parameters: +``` +Example with pointer to member function with additional parameters: ```c++ struct foo { @@ -555,7 +556,7 @@ You can also use `std::bind` to bind the this pointer and any parameter placehol If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. If you wish to provide a `constexpr` array, you will need to use a simple function prototype, since `std::function` is not constexpr (see unit tests for examples). -> [!TIP] +> [!IMPORTANT] > Your `std::array` of `std::tuple` should be sorted by enum. > The `dispatch` method performs a binary search on the array. Complexity for a sorted array is at most $2log_2(N)+O(1)$ comparisons. > If the array is _not_ sorted, complexity is linear. @@ -1034,7 +1035,7 @@ template // specialisation for member 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`). +Call supplied invocable for _every 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. You can limit the number of calls to your invocable 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. @@ -1043,7 +1044,7 @@ If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. Returns `std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...)` or `std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)` which can be stored or immediately invoked. -To iterate over each bit regardless of whether it is on or not, use `conjure_enum::for_each`. +To iterate over every bit regardless of whether it is on or not, use `conjure_enum::for_each`. Example using member function: ```c++ @@ -1076,6 +1077,36 @@ numbers::two numbers::five ``` +### iv. Using `conjure_enum::dispatch` with `enum_bitset` +Using an `enum_bitset` wth `conjure_enum::dispatch` can be a convenient way of iterating through a set of bits to call specific functions using `for_each`. The following demonstrates this: +```c++ +const auto dd3 +{ + std::to_array>> + ({ + { numbers::one, [](numbers ev, int a) + { std::cout << 1000 + a + conjure_enum::enum_to_int(ev) << '\n'; } }, + { numbers::two, [](numbers ev, int a) + { std::cout << 2000 + a + conjure_enum::enum_to_int(ev) << '\n'; } }, + { numbers::three, [](numbers ev, int a) + { std::cout << 3000 + a + conjure_enum::enum_to_int(ev) << '\n'; } }, + { static_cast(-1), [](numbers ev, [[maybe_unused]] int a) + { std::cout << "not found: " << conjure_enum::enum_to_int(ev) << '\n'; } }, // not found func + }) +}; +enum_bitset(1,2,3,5).for_each([](numbers val, const auto& arr, int num) +{ + conjure_enum::dispatch(val, arr, num); +}, dd3, 100); +``` +_output_ +```CSV +1101 +2102 +3103 +not found: 5 +``` + --- # 5. API and Examples using `conjure_type` `conjure_type` is a general purpose class allowing you to extract a string representation of any typename. @@ -1449,7 +1480,7 @@ It can be observed that there is only _one_ copy of the scoped enum value string | :--- | :--- | :--- | ---: | | [gcc](https://gcc.gnu.org/projects/cxx-status.html) | `11`, `12`, `13`, `14`| `std::format` not complete in `11`, `12` | `<= 10` | | [clang](https://clang.llvm.org/cxx_status.html) | `15`, `16`, `17`, `18`| Catch2 needs `cxx_std_20` in `15` | `<= 14` | -| [msvc](https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance) | `16`, `17` | Visual Studio 2019,2022, latest `17.10.3`| `<= 16.9`| +| [msvc](https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance) | `16`, `17` | Visual Studio 2019,2022, latest `17.10.4`| `<= 16.9`| | [xcode](https://developer.apple.com/support/xcode/) | `15` | Apple LLVM 15.0.0, some issues with `constexpr`, workarounds| `<= 14`| # 9. Compiler issues diff --git a/examples/srcloctest.cpp b/examples/srcloctest.cpp index f3e5fd70..61da5a0c 100644 --- a/examples/srcloctest.cpp +++ b/examples/srcloctest.cpp @@ -158,7 +158,16 @@ int main(int argc, char **argv) result->second ^= true; if (hlp) { - std::cout << "Usage: " << argv[0] << " [-cmh]" << R"( + std::cout << R"( +This program shows the output generated by your compiler. There are nine different +sets of tests - conjure_enum and conjure_type use this information to inform the +parsing algorithm to extract enum and type info. For most compilers, there will be +few if any differences between compiler versions - certainly few that impact +conjure_enum - however there have been a couple of changes with earlier releases. +Since the output of std::source_location is entirely implementation dependent, future +changes may occur. + +Usage: )" << argv[0] << " [-cmh]" << R"( -c show compiler (default true) -m output using markdown -h help diff --git a/utests/unittests.cpp b/utests/unittests.cpp index 43e15425..e8208cc5 100644 --- a/utests/unittests.cpp +++ b/utests/unittests.cpp @@ -232,6 +232,15 @@ TEST_CASE("iterators") REQUIRE(std::get(conjure_enum::back()) == std::get(*conjure_enum::crbegin())); } +//----------------------------------------------------------------------------------------- +TEST_CASE("iterator_adaptor") +{ + int tot{}; + for (const auto& itr : iterator_adaptor()) + tot += static_cast(std::get<0>(itr)); + REQUIRE(tot == 60); +} + //----------------------------------------------------------------------------------------- TEST_CASE("string_to_enum") { @@ -414,7 +423,7 @@ TEST_CASE("dispatch") REQUIRE(total1 == -1); // test empty - const std::array>, 0> dd4; + const std::array>, 0> dd4{}; REQUIRE(conjure_enum::dispatch(component::path, -1, dd4) == -1); const std::array>, 1> dd5 @@ -457,7 +466,7 @@ TEST_CASE("constexpr dispatch") struct foo { - int process(component val, int aint) + int process(component val, int aint) const { return aint * static_cast(val); } @@ -465,7 +474,7 @@ TEST_CASE("constexpr dispatch") foo bar; constexpr auto dd2a { - std::to_array> + std::to_array> ({ { component::scheme, &foo::process }, { component::port, &foo::process }, @@ -475,7 +484,7 @@ TEST_CASE("constexpr dispatch") REQUIRE(conjure_enum::dispatch(component::port, -1, dd2a, &bar, 1000) == 6000); // test empty - constexpr std::array, 0> dd4; + constexpr std::array, 0> dd4{}; REQUIRE(conjure_enum::dispatch(component::path, -1, dd4) == -1); constexpr std::array, 1> dd5 @@ -645,3 +654,57 @@ numbers::five(5) REQUIRE(total == 9); } +//----------------------------------------------------------------------------------------- +TEST_CASE("enum_bitset using conjure_enum::dispatch") +{ + struct foo + { + int total{}; + int process(numbers val, int aint) const + { + return aint * static_cast(val); + } + int process1(numbers val, int aint) const + { + return aint + static_cast(val); + } + int process2(numbers val, int aint) const + { + return aint - static_cast(val); + } + }; + constexpr auto tarr + { + std::to_array> + ({ + { numbers::two, &foo::process }, + { numbers::three, &foo::process1 }, + { numbers::four, &foo::process2 }, + }) + }; + foo bar; + enum_bitset enc(numbers::two,numbers::three,numbers::four,numbers::five); + enc.for_each([](numbers val, const auto& arr, foo *ptr, int extr) + { + ptr->total += conjure_enum::dispatch(val, -1, arr, ptr, extr); + }, tarr, &bar, 1000); + REQUIRE(bar.total == 3998); + + const auto dd2 + { + std::to_array>> + ({ + { numbers::one, [](numbers ev, int& a) { a += 1000 + conjure_enum::enum_to_int(ev); } }, + { numbers::two, [](numbers ev, int& a) { a += 2000 + conjure_enum::enum_to_int(ev); } }, + { numbers::three, [](numbers ev, int& a) { a += 3000 + conjure_enum::enum_to_int(ev); } }, + { static_cast(-1), []([[maybe_unused]] numbers ev, int& a) { a += -1; } }, // not found func + }) + }; + int total{}; + enum_bitset(1,2,3,5).for_each([](numbers val, const auto& arr, int& tot) + { + conjure_enum::dispatch(val, arr, tot); + }, dd2, std::ref(total)); + REQUIRE(total == 6005); +} +