From 62d47b0093a5549ca112e76249652a773b3fa825 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 10:17:17 +1000 Subject: [PATCH 01/20] updated --- README.md | 1 + reference/source_location.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 60a595f9..ba775b77 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ unlocked the potential of `constexpr` algorithms and concepts. This translates t - `remove_scope` - `unscoped_string_to_enum` - `for_each_n` + - `dispatch` - iterators and more! - ***Transparency***: Compiler implementation variability fully documented, verifiable and reportable (see 9 above) diff --git a/reference/source_location.md b/reference/source_location.md index 34eb6ff9..371a68ae 100644 --- a/reference/source_location.md +++ b/reference/source_location.md @@ -457,7 +457,7 @@ static consteval const char* conjure_type::tpeek() [with T = TEST::TEST1::Nin ``` --- -# Compiler: Clang: Ubuntu Clang 18.1.3 (1) +# Compiler: Clang: Ubuntu Clang 18.1.3 (1ubuntu1) ## 1. scoped enum ```c++ static const char *conjure_type::tpeek() [T = Namespace_Enum_Type] From 9d0bbca3ece938268782e6eaab29805beb420063 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 10:17:31 +1000 Subject: [PATCH 02/20] pre-rel 1.0i --- include/fix8/conjure_enum.hpp | 24 ++++++++ utests/unittests.cpp | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/include/fix8/conjure_enum.hpp b/include/fix8/conjure_enum.hpp index d21a070b..8a22b445 100644 --- a/include/fix8/conjure_enum.hpp +++ b/include/fix8/conjure_enum.hpp @@ -479,6 +479,30 @@ class conjure_enum : public static_only return for_each_n(n, std::bind(std::forward(func), obj, std::placeholders::_1, std::forward(args)...)); } + // dispatch + template + static constexpr bool tuple_comp(const std::tuple& pl, const std::tuple& pr) noexcept + { + return static_cast(std::get(pl)) < static_cast(std::get(pr)); + } + + template // specialisation with not found value(nval) for return + requires std::invocable + [[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, Args&&... args) noexcept + { + const auto result { std::equal_range(disp.cbegin(), disp.cend(), std::make_tuple(ev, Fn()), tuple_comp) }; + return result.first != result.second ? std::invoke(std::get(*result.first), ev, std::forward(args)...) : nval; + } + + template // specialisation for void func with not found call to last element + requires (std::invocable && I > 0) + static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args) noexcept + { + const auto result { std::equal_range(disp.cbegin(), std::prev(disp.cend()), std::make_tuple(ev, Fn()), tuple_comp) }; + return result.first != result.second ? std::invoke(std::get(*result.first), ev, std::forward(args)...) + : std::invoke(std::get(*std::prev(disp.cend())), ev, std::forward(args)...); + } + // public constexpr data structures static constexpr auto values { _values() }; static constexpr auto entries { _entries(std::make_index_sequence()) }; diff --git a/utests/unittests.cpp b/utests/unittests.cpp index 6c3804c8..91017d3a 100644 --- a/utests/unittests.cpp +++ b/utests/unittests.cpp @@ -353,6 +353,108 @@ TEST_CASE("for_each_n") REQUIRE(total == 33); } +//----------------------------------------------------------------------------------------- +TEST_CASE("dispatch") +{ + const auto dd1 + { + std::to_array>> + ({ + { component::scheme, [](component ev, int a) { return a * 100 + conjure_enum::enum_to_int(ev); } }, + { component::port, [](component ev, int a) { return a * 200 + conjure_enum::enum_to_int(ev); } }, + { component::fragment, [](component ev, int a) { return a * 300 + conjure_enum::enum_to_int(ev); } }, + }) + }; + REQUIRE(conjure_enum::dispatch(component::port, -1, dd1, 10) == 2006); + + struct foo + { + int process(component val, int aint) + { + return aint * static_cast(val); + } + }; + foo bar; + const auto dd2 + { + std::to_array>> + ({ + { component::scheme, std::bind(&foo::process, &bar, std::placeholders::_1, std::placeholders::_2) }, + { component::port, std::bind(&foo::process, &bar, std::placeholders::_1, std::placeholders::_2) }, + { component::fragment, std::bind(&foo::process, &bar, std::placeholders::_1, std::placeholders::_2) }, + }) + }; + REQUIRE(conjure_enum::dispatch(component::port, -1, dd2, 1000) == 6000); + + const auto dd3 + { + std::to_array>> + ({ + { component::scheme, [](component ev, int& a) { a += 1000 + conjure_enum::enum_to_int(ev); } }, + { component::port, [](component ev, int& a) { a += 2000 + conjure_enum::enum_to_int(ev); } }, + { component::fragment, [](component ev, int& a) { a += 3000 + conjure_enum::enum_to_int(ev); } }, + { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func + }) + }; + int total1{}; + conjure_enum::dispatch(component::port, dd3, std::ref(total1)); + REQUIRE(total1 == 2006); + conjure_enum::dispatch(component::path, dd3, std::ref(total1)); + REQUIRE(total1 == -1); + + // test empty + const std::array>, 0> dd4; + REQUIRE(conjure_enum::dispatch(component::path, -1, dd4) == -1); + + const std::array>, 1> dd5 + {{{ static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; }}}}; + total1 = 0; + conjure_enum::dispatch(component::path, dd5, std::ref(total1)); + REQUIRE(total1 == -1); +} + +//----------------------------------------------------------------------------------------- +TEST_CASE("constexpr dispatch") +{ + constexpr auto dd1 + { + std::to_array> + ({ + { component::scheme, [](component ev, int a) { return a * 1000 + conjure_enum::enum_to_int(ev); } }, + { component::port, [](component ev, int a) { return a * 2000 + conjure_enum::enum_to_int(ev); } }, + { component::fragment, [](component ev, int a) { return a * 3000 + conjure_enum::enum_to_int(ev); } }, + }) + }; + REQUIRE(conjure_enum::dispatch(component::port, -1, dd1, 1000) == 2000006); + REQUIRE(conjure_enum::dispatch(component::path, -1, dd1, 1000) == -1); + + constexpr auto dd2 + { + std::to_array> + ({ + { component::scheme, [](component ev, int& a) { a += 1000 + conjure_enum::enum_to_int(ev); } }, + { component::port, [](component ev, int& a) { a += 2000 + conjure_enum::enum_to_int(ev); } }, + { component::fragment, [](component ev, int& a) { a += 3000 + conjure_enum::enum_to_int(ev); } }, + { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func + }) + }; + int total{}; + conjure_enum::dispatch(component::port, dd2, std::ref(total)); + REQUIRE(total == 2006); + conjure_enum::dispatch(component::path, dd2, std::ref(total)); + REQUIRE(total == -1); + + // test empty + constexpr std::array, 0> dd4; + REQUIRE(conjure_enum::dispatch(component::path, -1, dd4) == -1); + + constexpr std::array, 1> dd5 + {{{ static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; }}}}; + int total1{}; + conjure_enum::dispatch(component::path, dd5, std::ref(total1)); + REQUIRE(total1 == -1); +} + //----------------------------------------------------------------------------------------- TEST_CASE("enum_bitset") { From c30b6c1683bac723b10ced0246c9450804fe05b8 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:05:05 +1000 Subject: [PATCH 03/20] updated --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ba775b77..9620926a 100644 --- a/README.md +++ b/README.md @@ -519,7 +519,51 @@ _output_ ```CSV 160 ``` -## o) `is_scoped` +## o) `dispatch` +```c++ +template +static constexpr bool tuple_comp(const std::tuple& pl, const std::tuple& pr); + +template // specialisation with not found value(nval) for return +requires std::invocable +[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, Args&&... args); + +template // specialisation for void func with not found call to last element +requires (std::invocable && I > 0) +static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args); +``` +With a given enum, search and call user supplied invokable. Where invokable returns a value, return this value or a user supplied "not found" value. +Where invokable is void, call user supplied "not found" invokable. +The first parameter of your invocable must accept an enum value (passed by `dispatch`). +Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. +To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. +If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. +There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invokable. +The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. +This version is intended for use with void invokables. +> [!TIP] +> 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 $\2log_2(N)+O(1)$ +> If the array is _not_ sorted, complexity is linear. +```c++ +conjure_enum::for_each([](component val, int other) +{ + std::cout << static_cast(val) << ' ' << other << '\n'; +}, 10); +``` +_output_ +```CSV +0 10 +1 10 +2 10 +3 10 +4 10 +5 10 +6 10 +12 10 +13 10 +14 10 +``` +## p) `is_scoped` ```c++ struct is_scoped : std::integral_constant>; }>{}; @@ -534,7 +578,7 @@ _output_ true false ``` -## p) `is_valid` +## q) `is_valid` ```c++ template static constexpr bool is_valid(); @@ -549,7 +593,7 @@ _output_ true false ``` -## q) `type_name` +## r) `type_name` ```c++ static constexpr std::string_view type_name(); ``` @@ -563,7 +607,7 @@ _output_ component component1 ``` -## r) `remove_scope` +## s) `remove_scope` ```c++ static constexpr std::string_view remove_scope(std::string_view what); ``` @@ -577,7 +621,7 @@ _output_ path path ``` -## s) `add_scope` +## t) `add_scope` ```c++ static constexpr std::string_view add_scope(std::string_view what); ``` @@ -591,7 +635,7 @@ _output_ component::path path ``` -## t) `has_scope` +## u) `has_scope` ```c++ static constexpr bool has_scope(std::string_view what); ``` @@ -607,7 +651,7 @@ true false false ``` -## u) `iterators` +## v) `iterators` ```c++ static constexpr auto cbegin(); static constexpr auto cend(); @@ -634,7 +678,7 @@ _output_ 8 numbers::eight 9 numbers::nine ``` -## v) `iterator_adaptor` +## w) `iterator_adaptor` ```c++ template struct iterator_adaptor; @@ -657,7 +701,7 @@ _output_ 8 9 ``` -## w) `front, back` +## x) `front, back` ```c++ static constexpr auto front(); static constexpr auto back(); @@ -674,7 +718,7 @@ _output_ 0 numbers::zero 9 numbers::nine ``` -## x) `ostream_enum_operator` +## y) `ostream_enum_operator` ```c++ template, valid_enum T> constexpr std::basic_ostream& operator<<(std::basic_ostream& os, T value); @@ -696,7 +740,7 @@ _output_ "host" "100" ``` -## y) `epeek, tpeek` +## z) `epeek, tpeek` ```c++ static consteval const char *tpeek(); template From b6cf3123d64dcd6429bf8133c4a35490688ca4d7 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:05:37 +1000 Subject: [PATCH 04/20] updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9620926a..a0744e91 100644 --- a/README.md +++ b/README.md @@ -542,7 +542,7 @@ There are two versions of `dispatch` - the first takes an enum value, a 'not fou The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. This version is intended for use with void invokables. > [!TIP] -> 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 $\2log_2(N)+O(1)$ +> 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 $2log_2(N)+O(1)$ > If the array is _not_ sorted, complexity is linear. ```c++ conjure_enum::for_each([](component val, int other) From d9bc396b87c90e00cc65c8386a81999b160706d3 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:06:35 +1000 Subject: [PATCH 05/20] updated --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a0744e91..03fc0794 100644 --- a/README.md +++ b/README.md @@ -542,7 +542,8 @@ There are two versions of `dispatch` - the first takes an enum value, a 'not fou The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. This version is intended for use with void invokables. > [!TIP] -> 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 $2log_2(N)+O(1)$ +> 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 $2log_2(N)+O(1)$. > If the array is _not_ sorted, complexity is linear. ```c++ conjure_enum::for_each([](component val, int other) From 321d8b8401299b655e2fcb100618ab6e8de8bf76 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:07:21 +1000 Subject: [PATCH 06/20] updated --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 03fc0794..28433bbb 100644 --- a/README.md +++ b/README.md @@ -538,6 +538,7 @@ The first parameter of your invocable must accept an enum value (passed by `disp Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. + There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invokable. The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. This version is intended for use with void invokables. From bc1b686aade0514e56c7b3eaf13da0d70f5c18d8 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:09:46 +1000 Subject: [PATCH 07/20] updated --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 28433bbb..08d7d16d 100644 --- a/README.md +++ b/README.md @@ -541,10 +541,10 @@ If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invokable. The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. -This version is intended for use with void invokables. +This version is intended for use with `void` return invokables. > [!TIP] > 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 $2log_2(N)+O(1)$. +> 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. ```c++ conjure_enum::for_each([](component val, int other) From cd2566a7d1d427effb42459ee772d4cd77a69b9a Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:11:26 +1000 Subject: [PATCH 08/20] updated --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 08d7d16d..eea72cd6 100644 --- a/README.md +++ b/README.md @@ -435,7 +435,7 @@ requires std::invocable ``` 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. 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 +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. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. @@ -532,16 +532,16 @@ template // specialisation for voi requires (std::invocable && I > 0) static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args); ``` -With a given enum, search and call user supplied invokable. Where invokable returns a value, return this value or a user supplied "not found" value. -Where invokable is void, call user supplied "not found" invokable. +With a given enum, search and call user supplied invocable. Where invocable returns a value, return this value or a user supplied "not found" value. +Where invocable is void, call user supplied "not found" invocable. The first parameter of your invocable must accept an enum value (passed by `dispatch`). Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. -There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invokable. -The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invokable. The last element of the array is called if the enum is not found. -This version is intended for use with `void` return invokables. +There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invocable. +The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invocable. The last element of the array is called if the enum is not found. +This version is intended for use with `void` return invocables. > [!TIP] > 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. @@ -984,7 +984,7 @@ requires std::invocable ``` 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. 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 +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. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. From a6d006a7f5f77077e8de8826008202d4093be5a0 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:20:42 +1000 Subject: [PATCH 09/20] updated --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index eea72cd6..90980795 100644 --- a/README.md +++ b/README.md @@ -539,6 +539,8 @@ Optionally provide any additional parameters. Works with lambdas, member functio To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. +This method is intended to be used where complex event handling is required, with predefined actions as methods, functions or lambdas. + There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invocable. The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invocable. The last element of the array is called if the enum is not found. This version is intended for use with `void` return invocables. From c36690becc2e7e7151fcf7b5d6fa461dfb3ed32e Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 12:35:47 +1000 Subject: [PATCH 10/20] updated --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 90980795..7d78668a 100644 --- a/README.md +++ b/README.md @@ -532,18 +532,20 @@ template // specialisation for voi requires (std::invocable && I > 0) static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args); ``` -With a given enum, search and call user supplied invocable. Where invocable returns a value, return this value or a user supplied "not found" value. +With a given enum, search and call user supplied invocable. Use this method where complex event handling is required, with predefined actions as methods, functions or lambdas. + +Where invocable returns a value, return this value or a user supplied "not found" value. Where invocable is void, call user supplied "not found" invocable. The first parameter of your invocable must accept an enum value (passed by `dispatch`). Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. -This method is intended to be used where complex event handling is required, with predefined actions as methods, functions or lambdas. - There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invocable. The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invocable. The last element of the array is called if the enum is not found. This version is intended for use with `void` return invocables. + +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] > 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. From 48545f06bb2c4a0397452b80b5408abd2997deff Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 14:34:09 +1000 Subject: [PATCH 11/20] updated --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7d78668a..e57b640a 100644 --- a/README.md +++ b/README.md @@ -551,23 +551,26 @@ If you wish to provide a `constexpr` array, you will need to use a simple functi > 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. ```c++ -conjure_enum::for_each([](component val, int other) +const auto tarr { - std::cout << static_cast(val) << ' ' << other << '\n'; -}, 10); + std::to_array>> + ({ + { component::scheme, [](component ev, int& a) { a += 1000; } }, + { component::port, [](component ev, int& a) { a += 2000; } }, + { component::fragment, [](component ev, int& a) { a += 3000; } }, + { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func + }) +}; +int total{}; +conjure_enum::dispatch(component::port, tarr, std::ref(total)); +std::cout << total << '\n'; +conjure_enum::dispatch(component::path, tarr, std::ref(total)); +std::cout << total << '\n'; ``` _output_ ```CSV -0 10 -1 10 -2 10 -3 10 -4 10 -5 10 -6 10 -12 10 -13 10 -14 10 +2006 +-1 ``` ## p) `is_scoped` ```c++ From 96c0c9aa321ae3cd74ec15904b97c8fb353723e4 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 14:34:34 +1000 Subject: [PATCH 12/20] updated --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e57b640a..940fd9ef 100644 --- a/README.md +++ b/README.md @@ -553,13 +553,13 @@ If you wish to provide a `constexpr` array, you will need to use a simple functi ```c++ const auto tarr { - std::to_array>> - ({ - { component::scheme, [](component ev, int& a) { a += 1000; } }, - { component::port, [](component ev, int& a) { a += 2000; } }, - { component::fragment, [](component ev, int& a) { a += 3000; } }, - { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func - }) + std::to_array>> + ({ + { component::scheme, [](component ev, int& a) { a += 1000; } }, + { component::port, [](component ev, int& a) { a += 2000; } }, + { component::fragment, [](component ev, int& a) { a += 3000; } }, + { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func + }) }; int total{}; conjure_enum::dispatch(component::port, tarr, std::ref(total)); From 8b3cf238ca5e38b4faa4427e1afa86435ab74e79 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 14:35:17 +1000 Subject: [PATCH 13/20] updated --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 940fd9ef..4656d408 100644 --- a/README.md +++ b/README.md @@ -555,9 +555,9 @@ const auto tarr { std::to_array>> ({ - { component::scheme, [](component ev, int& a) { a += 1000; } }, - { component::port, [](component ev, int& a) { a += 2000; } }, - { component::fragment, [](component ev, int& a) { a += 3000; } }, + { component::scheme, [](component ev, int& a) { a = 1000; } }, + { component::port, [](component ev, int& a) { a = 2000; } }, + { component::fragment, [](component ev, int& a) { a = 3000; } }, { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func }) }; From db354fd5a7ca5b3ce54c47cb2ab890097c1a769b Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 14:54:40 +1000 Subject: [PATCH 14/20] updated --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4656d408..db0513d7 100644 --- a/README.md +++ b/README.md @@ -555,10 +555,10 @@ const auto tarr { std::to_array>> ({ - { component::scheme, [](component ev, int& a) { a = 1000; } }, - { component::port, [](component ev, int& a) { a = 2000; } }, - { component::fragment, [](component ev, int& a) { a = 3000; } }, - { static_cast(-1), []([[maybe_unused]] component ev, int& a) { a = -1; } }, // not found func + { component::scheme, [](component ev, int& a) { a = static_cast(ev) + 1000; } }, + { component::port, [](component ev, int& a) { a = static_cast(ev) + 2000; } }, + { component::fragment, [](component ev, int& a) { a = static_cast(ev) + 3000; } }, + { static_cast(-1), [](component ev, int& a) { a = static_cast(ev); } }, // not found func }) }; int total{}; From e81531ae5314f6c91b7ae3bba98b0aada4f9fe78 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sun, 7 Jul 2024 14:57:06 +1000 Subject: [PATCH 15/20] updated --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index db0513d7..65dc0093 100644 --- a/README.md +++ b/README.md @@ -500,8 +500,7 @@ _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 { @@ -532,7 +531,8 @@ template // specialisation for voi requires (std::invocable && I > 0) static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args); ``` -With a given enum, search and call user supplied invocable. Use this method where complex event handling is required, with predefined actions as methods, functions or lambdas. +With a given enum, search and call user supplied invocable. Use this method where complex event handling is required, allowing you to easily declare predefined invocable actions +for different enum values. Where invocable returns a value, return this value or a user supplied "not found" value. Where invocable is void, call user supplied "not found" invocable. From d64f2cf8ae330859651a03299e91be968d670712 Mon Sep 17 00:00:00 2001 From: David Dight Date: Mon, 8 Jul 2024 09:01:41 +1000 Subject: [PATCH 16/20] updated --- README.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 65dc0093..699e5f95 100644 --- a/README.md +++ b/README.md @@ -551,25 +551,28 @@ If you wish to provide a `constexpr` array, you will need to use a simple functi > 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. ```c++ -const auto tarr +enum class directions { left, right, up, down, forward, backward, notfound=-1 }; +static constexpr auto prn([](directions ev) { std::cout << conjure_enum::enum_to_string(ev) << '\n'; }); +static constexpr auto tarr { - std::to_array>> + std::to_array> ({ - { component::scheme, [](component ev, int& a) { a = static_cast(ev) + 1000; } }, - { component::port, [](component ev, int& a) { a = static_cast(ev) + 2000; } }, - { component::fragment, [](component ev, int& a) { a = static_cast(ev) + 3000; } }, - { static_cast(-1), [](component ev, int& a) { a = static_cast(ev); } }, // not found func + { directions::left, prn }, + { directions::right, prn }, + { directions::up, prn }, + { directions::down, prn }, + { directions::backward, prn }, + { directions::notfound, [](directions ev) { std::cout << "not found: "; prn(ev); } }, // not found func }) }; -int total{}; -conjure_enum::dispatch(component::port, tarr, std::ref(total)); -std::cout << total << '\n'; -conjure_enum::dispatch(component::path, tarr, std::ref(total)); -std::cout << total << '\n'; +conjure_enum::dispatch(directions::right, tarr); +conjure_enum::dispatch(directions::forward, tarr); +std::cout << conjure_enum::enum_to_int(directions::notfound) << '\n'; ``` _output_ ```CSV -2006 +directions::right +not found: directions::forward -1 ``` ## p) `is_scoped` From 8b799fc5284b1c5f531f9a915ae47af4b40cdf73 Mon Sep 17 00:00:00 2001 From: David Dight Date: Tue, 9 Jul 2024 09:37:17 +1000 Subject: [PATCH 17/20] updated --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 699e5f95..c5d125e4 100644 --- a/README.md +++ b/README.md @@ -523,13 +523,21 @@ _output_ template static constexpr bool tuple_comp(const std::tuple& pl, const std::tuple& pr); -template // specialisation with not found value(nval) for return +template // with not found value(nval) for return requires std::invocable [[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, Args&&... args); -template // specialisation for void func with not found call to last element +template // specialisation for member function with not found value(nval) for return +requires std::invocable +[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, C *obj, Args&&... args); + +template // void func with not found call to last element requires (std::invocable && I > 0) static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args); + +template // specialisation for void member function with not found call to last element +requires (std::invocable && I > 0) +static constexpr void dispatch(T ev, const std::array, I>& disp, C *obj, Args&&... args); ``` With a given enum, search and call user supplied invocable. Use this method where complex event handling is required, allowing you to easily declare predefined invocable actions for different enum values. @@ -538,12 +546,13 @@ Where invocable returns a value, return this value or a user supplied "not found Where invocable is void, call user supplied "not found" invocable. The first parameter of your invocable must accept an enum value (passed by `dispatch`). Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. -To user member functions, use `std::bind` to bind the this pointer and any parameter placeholders. -If you wish to pass a `reference` parameter, you must wrap it in `std::ref`. There are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invocable. The second version takes an enum value, and a `std::array` of `std::tuple` of enum and invocable. The last element of the array is called if the enum is not found. This version is intended for use with `void` return invocables. +The second version of each of the above 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. +You can also use `std::bind` to bind the this pointer and any parameter placeholders when declaring your array. +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] @@ -575,6 +584,35 @@ directions::right not found: directions::forward -1 ``` +This example uses member functions: +```c++ +struct foo +{ + int process(component val, int aint) + { + return aint * static_cast(val); + } + int process1(component val, int aint) + { + return aint + static_cast(val); + } +}; +constexpr auto tarr1 +{ + std::to_array> + ({ + { component::scheme, &foo::process }, + { component::port, &foo::process }, + { component::fragment, &foo::process1 }, + }) +}; +foo bar; +std::cout << conjure_enum::dispatch(component::port, -1, tarr1, &bar, 1000) << '\n'; +``` +_output_ +```CSV +6000 +``` ## p) `is_scoped` ```c++ struct is_scoped : std::integral_constant Date: Tue, 9 Jul 2024 13:44:53 +1000 Subject: [PATCH 18/20] updated --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c5d125e4..080a7877 100644 --- a/README.md +++ b/README.md @@ -608,10 +608,12 @@ constexpr auto tarr1 }; foo bar; std::cout << conjure_enum::dispatch(component::port, -1, tarr1, &bar, 1000) << '\n'; +std::cout << conjure_enum::dispatch(component::path, -1, tarr1, &bar, 1000) << '\n'; ``` _output_ ```CSV 6000 +-1 ``` ## p) `is_scoped` ```c++ From 3121fbdbe39454e9f8de6b031bb3ad1a8fe12946 Mon Sep 17 00:00:00 2001 From: David Dight Date: Tue, 9 Jul 2024 13:45:01 +1000 Subject: [PATCH 19/20] pre-rel 1.0i --- include/fix8/conjure_enum.hpp | 20 ++++++++++++-- utests/unittests.cpp | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/include/fix8/conjure_enum.hpp b/include/fix8/conjure_enum.hpp index 8a22b445..8177ee17 100644 --- a/include/fix8/conjure_enum.hpp +++ b/include/fix8/conjure_enum.hpp @@ -486,7 +486,7 @@ class conjure_enum : public static_only return static_cast(std::get(pl)) < static_cast(std::get(pr)); } - template // specialisation with not found value(nval) for return + template // with not found value(nval) for return requires std::invocable [[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, Args&&... args) noexcept { @@ -494,7 +494,15 @@ class conjure_enum : public static_only return result.first != result.second ? std::invoke(std::get(*result.first), ev, std::forward(args)...) : nval; } - template // specialisation for void func with not found call to last element + template // specialisation for member function with not found value(nval) for return + requires std::invocable + [[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array, I>& disp, C *obj, Args&&... args) noexcept + { + const auto result { std::equal_range(disp.cbegin(), disp.cend(), std::make_tuple(ev, Fn()), tuple_comp) }; + return result.first != result.second ? std::invoke(std::get(*result.first), obj, ev, std::forward(args)...) : nval; + } + + template // void func with not found call to last element requires (std::invocable && I > 0) static constexpr void dispatch(T ev, const std::array, I>& disp, Args&&... args) noexcept { @@ -503,6 +511,14 @@ class conjure_enum : public static_only : std::invoke(std::get(*std::prev(disp.cend())), ev, std::forward(args)...); } + template // specialisation for void member function with not found call to last element + requires (std::invocable && I > 0) + static constexpr void dispatch(T ev, const std::array, I>& disp, C *obj, Args&&... args) noexcept + { + const auto result { std::equal_range(disp.cbegin(), std::prev(disp.cend()), std::make_tuple(ev, Fn()), tuple_comp) }; + return result.first != result.second ? std::invoke(std::get(*result.first), obj, ev, std::forward(args)...) + : std::invoke(std::get(*std::prev(disp.cend())), obj, ev, std::forward(args)...); + } // public constexpr data structures static constexpr auto values { _values() }; static constexpr auto entries { _entries(std::make_index_sequence()) }; diff --git a/utests/unittests.cpp b/utests/unittests.cpp index 91017d3a..43e15425 100644 --- a/utests/unittests.cpp +++ b/utests/unittests.cpp @@ -44,6 +44,7 @@ using namespace std::literals::string_literals; enum class component : int { scheme, authority, userinfo, user, password, host, port, path=12, test=path, query, fragment }; enum component1 : int { scheme, authority, userinfo, user, password, host, port, path=12, query, fragment }; enum class numbers : int { zero, one, two, three, four, five, FIVE=five, six, seven, eight, nine }; +enum class directions { left, right, up, down, forward, backward, notfound=-1 }; //----------------------------------------------------------------------------------------- // run as: ctest --output-on-failure @@ -385,6 +386,16 @@ TEST_CASE("dispatch") }) }; REQUIRE(conjure_enum::dispatch(component::port, -1, dd2, 1000) == 6000); + const auto dd2a + { + std::to_array> + ({ + { component::scheme, &foo::process }, + { component::port, &foo::process }, + { component::fragment, &foo::process }, + }) + }; + REQUIRE(conjure_enum::dispatch(component::port, -1, dd2a, &bar, 1000) == 6000); const auto dd3 { @@ -444,6 +455,25 @@ TEST_CASE("constexpr dispatch") conjure_enum::dispatch(component::path, dd2, std::ref(total)); REQUIRE(total == -1); + struct foo + { + int process(component val, int aint) + { + return aint * static_cast(val); + } + }; + foo bar; + constexpr auto dd2a + { + std::to_array> + ({ + { component::scheme, &foo::process }, + { component::port, &foo::process }, + { component::fragment, &foo::process }, + }) + }; + REQUIRE(conjure_enum::dispatch(component::port, -1, dd2a, &bar, 1000) == 6000); + // test empty constexpr std::array, 0> dd4; REQUIRE(conjure_enum::dispatch(component::path, -1, dd4) == -1); @@ -453,6 +483,25 @@ TEST_CASE("constexpr dispatch") int total1{}; conjure_enum::dispatch(component::path, dd5, std::ref(total1)); REQUIRE(total1 == -1); + + static constexpr auto prn([](directions ev, int& a) { a = conjure_enum::enum_to_int(ev); }); + static constexpr auto tarr + { + std::to_array> + ({ + { directions::left, prn }, + { directions::right, prn }, + { directions::up, prn }, + { directions::down, prn }, + { directions::backward, prn }, + { directions::notfound, []([[maybe_unused]] directions ev, int& a) { a = -1; } }, // not found func + }) + }; + int val; + conjure_enum::dispatch(directions::right, tarr, std::ref(val)); + REQUIRE(val == 1); + conjure_enum::dispatch(directions::forward, tarr, std::ref(val)); + REQUIRE(val == -1); } //----------------------------------------------------------------------------------------- From a4e0438f840c8fee702bda58692b72bd6a97f39e Mon Sep 17 00:00:00 2001 From: David Dight Date: Tue, 9 Jul 2024 15:50:04 +1000 Subject: [PATCH 20/20] updated --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 080a7877..e9aa5fc6 100644 --- a/README.md +++ b/README.md @@ -607,13 +607,15 @@ constexpr auto tarr1 }) }; foo bar; -std::cout << conjure_enum::dispatch(component::port, -1, tarr1, &bar, 1000) << '\n'; -std::cout << conjure_enum::dispatch(component::path, -1, tarr1, &bar, 1000) << '\n'; +for (auto val : { component::scheme, component::path, component::port, component::fragment }) + std::cout << conjure_enum::dispatch(val, -1, tarr1, &bar, 1000) << '\n'; ``` _output_ ```CSV -6000 +0 -1 +6000 +1015 ``` ## p) `is_scoped` ```c++