Skip to content

Commit dbec81e

Browse files
drewdzzzalyapunov
authored andcommitted
Trait: introduce function visit
New function allows to call a functor on current alternative of arbitrary variant. Actually, the function mimics `std::visit`.
1 parent d2d3885 commit dbec81e

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

src/Utils/Traits.hpp

+50
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787

8888
#include <cstddef>
8989
#include <iterator>
90+
#include <functional>
9091
#include <tuple>
9192
#include <type_traits>
9293
#include <variant>
@@ -554,6 +555,55 @@ constexpr bool is_variant_v =
554555
!std::is_reference_v<std::remove_cv_t<T>> &&
555556
details::is_variant_h<std::remove_cv_t<T>>::value;
556557

558+
namespace details {
559+
/**
560+
* Class that have only one static method which invokes functor on I-th
561+
* alternative of variant.
562+
*/
563+
template <size_t I>
564+
struct VisitJumpGenerator {
565+
template<class F, class V>
566+
static constexpr void jump(F&& f, V&& v)
567+
{
568+
std::invoke(f, tnt::get<I>(v));
569+
}
570+
};
571+
572+
/**
573+
* Jump table for visitor - i-th element of data is a method that invokes
574+
* functor on i-th alternative of variant.
575+
*/
576+
template <class F, class V>
577+
struct VisitJumps {
578+
using jump_t = void (*)(F&&, V&&);
579+
using variant_t = std::remove_reference_t<V>;
580+
static constexpr size_t v_size = std::variant_size_v<variant_t>;
581+
using data_t = std::array<jump_t, v_size>;
582+
data_t data;
583+
584+
template <size_t... I>
585+
static constexpr data_t
586+
build_by_range(tnt::iseq<I...>)
587+
{
588+
return {(VisitJumpGenerator<I>::jump)...};
589+
}
590+
591+
constexpr VisitJumps() :
592+
data(build_by_range(tnt::make_iseq<v_size>{})) {}
593+
};
594+
};
595+
596+
/**
597+
* Invokes the functor on current alternative of variant.
598+
*/
599+
template <class F, class V>
600+
void visit(F &&f, V &&v)
601+
{
602+
static constexpr details::VisitJumps<F, V> jumps;
603+
auto visitor = jumps.data[v.index()];
604+
visitor(std::forward<F>(f), std::forward<V>(v));
605+
}
606+
557607
/**
558608
* Check whether the type looks like std::optional, at least it has
559609
* operator bool, operator *, bool has_value() and value() methods.

test/TraitsUnitTest.cpp

+70
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,75 @@ test_common()
10751075
static_assert(s5.get<3>() == 3);
10761076
}
10771077

1078+
void
1079+
test_visit()
1080+
{
1081+
using variant_t = std::variant<int, bool, std::string, double>;
1082+
1083+
variant_t variant = 10;
1084+
size_t test_count = 0;
1085+
tnt::visit([&](const auto &value) {
1086+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1087+
if constexpr (std::is_same_v<value_t, int>) {
1088+
fail_unless(value == 10);
1089+
test_count++;
1090+
}
1091+
}, variant);
1092+
1093+
variant = true;
1094+
tnt::visit([&](const auto &value) {
1095+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1096+
if constexpr (std::is_same_v<value_t, bool>) {
1097+
fail_unless(value);
1098+
test_count++;
1099+
}
1100+
}, variant);
1101+
1102+
variant = std::string("abc");
1103+
tnt::visit([&](const auto &value) {
1104+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1105+
if constexpr (std::is_same_v<value_t, std::string>) {
1106+
fail_unless(value == "abc");
1107+
test_count++;
1108+
}
1109+
}, variant);
1110+
1111+
variant = 3.0;
1112+
tnt::visit([&](const auto &value) {
1113+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1114+
if constexpr (std::is_same_v<value_t, double>) {
1115+
fail_unless(value == 3.0);
1116+
test_count++;
1117+
}
1118+
}, variant);
1119+
1120+
/* Check if all the tests were fired. */
1121+
fail_unless(test_count == 4);
1122+
1123+
test_count = 0;
1124+
CustomVariant custom_variant;
1125+
custom_variant.template emplace<0>(42);
1126+
tnt::visit([&](const auto &value) {
1127+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1128+
if constexpr (std::is_same_v<value_t, int>) {
1129+
fail_unless(value == 42);
1130+
test_count++;
1131+
}
1132+
}, custom_variant);
1133+
1134+
custom_variant.template emplace<1>(66.6);
1135+
tnt::visit([&](const auto &value) {
1136+
using value_t = std::remove_cv_t<std::remove_reference_t<decltype(value)>>;
1137+
if constexpr (std::is_same_v<value_t, double>) {
1138+
fail_unless(value == 66.6);
1139+
test_count++;
1140+
}
1141+
}, custom_variant);
1142+
1143+
/* Check if all the tests were fired. */
1144+
fail_unless(test_count == 2);
1145+
}
1146+
10781147
int main()
10791148
{
10801149
test_integer_traits();
@@ -1089,4 +1158,5 @@ int main()
10891158
test_rw_container_traits();
10901159
test_is_method_callable();
10911160
test_common();
1161+
test_visit();
10921162
}

0 commit comments

Comments
 (0)