Skip to content

Commit

Permalink
Merge pull request #11 from fix8mt/dev
Browse files Browse the repository at this point in the history
Dev pre1.0j to Master
  • Loading branch information
dakka authored Jul 17, 2024
2 parents 45a9d09 + b44f48a commit 3455f33
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 10 deletions.
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1034,7 +1035,7 @@ template<typename C, typename Fn, typename... Args> // specialisation for member
requires std::invocable<Fn&&, C, T, Args...>
[[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.
Expand All @@ -1043,7 +1044,7 @@ If you wish to pass a `reference` parameter, you must wrap it in `std::ref`.
Returns `std::bind(std::forward<Fn>(func), std::placeholders::_1, std::forward<Args>(args)...)` or
`std::bind(std::forward<Fn>(func), obj, std::placeholders::_1, std::forward<Args>(args)...)` which can be stored or immediately invoked.
To iterate over each bit regardless of whether it is on or not, use `conjure_enum<T>::for_each`.
To iterate over every bit regardless of whether it is on or not, use `conjure_enum<T>::for_each`.
Example using member function:
```c++
Expand Down Expand Up @@ -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<std::tuple<numbers, std::function<void(numbers, int)>>>
({
{ numbers::one, [](numbers ev, int a)
{ std::cout << 1000 + a + conjure_enum<numbers>::enum_to_int(ev) << '\n'; } },
{ numbers::two, [](numbers ev, int a)
{ std::cout << 2000 + a + conjure_enum<numbers>::enum_to_int(ev) << '\n'; } },
{ numbers::three, [](numbers ev, int a)
{ std::cout << 3000 + a + conjure_enum<numbers>::enum_to_int(ev) << '\n'; } },
{ static_cast<numbers>(-1), [](numbers ev, [[maybe_unused]] int a)
{ std::cout << "not found: " << conjure_enum<numbers>::enum_to_int(ev) << '\n'; } }, // not found func
})
};
enum_bitset<numbers>(1,2,3,5).for_each([](numbers val, const auto& arr, int num)
{
conjure_enum<numbers>::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.
Expand Down Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion examples/srcloctest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 67 additions & 4 deletions utests/unittests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ TEST_CASE("iterators")
REQUIRE(std::get<component1>(conjure_enum<component1>::back()) == std::get<component1>(*conjure_enum<component1>::crbegin()));
}

//-----------------------------------------------------------------------------------------
TEST_CASE("iterator_adaptor")
{
int tot{};
for (const auto& itr : iterator_adaptor<component>())
tot += static_cast<int>(std::get<0>(itr));
REQUIRE(tot == 60);
}

//-----------------------------------------------------------------------------------------
TEST_CASE("string_to_enum")
{
Expand Down Expand Up @@ -414,7 +423,7 @@ TEST_CASE("dispatch")
REQUIRE(total1 == -1);

// test empty
const std::array<std::tuple<component, std::function<int(component)>>, 0> dd4;
const std::array<std::tuple<component, std::function<int(component)>>, 0> dd4{};
REQUIRE(conjure_enum<component>::dispatch(component::path, -1, dd4) == -1);

const std::array<std::tuple<component, std::function<void(component, int&)>>, 1> dd5
Expand Down Expand Up @@ -457,15 +466,15 @@ TEST_CASE("constexpr dispatch")

struct foo
{
int process(component val, int aint)
int process(component val, int aint) const
{
return aint * static_cast<int>(val);
}
};
foo bar;
constexpr auto dd2a
{
std::to_array<std::tuple<component, int (foo::*)(component, int)>>
std::to_array<std::tuple<component, int (foo::*)(component, int) const>>
({
{ component::scheme, &foo::process },
{ component::port, &foo::process },
Expand All @@ -475,7 +484,7 @@ TEST_CASE("constexpr dispatch")
REQUIRE(conjure_enum<component>::dispatch(component::port, -1, dd2a, &bar, 1000) == 6000);

// test empty
constexpr std::array<std::tuple<component, int (*)(component)>, 0> dd4;
constexpr std::array<std::tuple<component, int (*)(component)>, 0> dd4{};
REQUIRE(conjure_enum<component>::dispatch(component::path, -1, dd4) == -1);

constexpr std::array<std::tuple<component, void (*)(component, int&)>, 1> dd5
Expand Down Expand Up @@ -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<int>(val);
}
int process1(numbers val, int aint) const
{
return aint + static_cast<int>(val);
}
int process2(numbers val, int aint) const
{
return aint - static_cast<int>(val);
}
};
constexpr auto tarr
{
std::to_array<std::tuple<numbers, int (foo::*)(numbers, int) const>>
({
{ numbers::two, &foo::process },
{ numbers::three, &foo::process1 },
{ numbers::four, &foo::process2 },
})
};
foo bar;
enum_bitset<numbers> 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<numbers>::dispatch(val, -1, arr, ptr, extr);
}, tarr, &bar, 1000);
REQUIRE(bar.total == 3998);

const auto dd2
{
std::to_array<std::tuple<numbers, std::function<void(numbers, int&)>>>
({
{ numbers::one, [](numbers ev, int& a) { a += 1000 + conjure_enum<numbers>::enum_to_int(ev); } },
{ numbers::two, [](numbers ev, int& a) { a += 2000 + conjure_enum<numbers>::enum_to_int(ev); } },
{ numbers::three, [](numbers ev, int& a) { a += 3000 + conjure_enum<numbers>::enum_to_int(ev); } },
{ static_cast<numbers>(-1), []([[maybe_unused]] numbers ev, int& a) { a += -1; } }, // not found func
})
};
int total{};
enum_bitset<numbers>(1,2,3,5).for_each([](numbers val, const auto& arr, int& tot)
{
conjure_enum<numbers>::dispatch(val, arr, tot);
}, dd2, std::ref(total));
REQUIRE(total == 6005);
}

0 comments on commit 3455f33

Please sign in to comment.