aboutsummaryrefslogtreecommitdiff
path: root/fcp/base/meta_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'fcp/base/meta_test.cc')
-rw-r--r--fcp/base/meta_test.cc397
1 files changed, 397 insertions, 0 deletions
diff --git a/fcp/base/meta_test.cc b/fcp/base/meta_test.cc
new file mode 100644
index 0000000..911cc8e
--- /dev/null
+++ b/fcp/base/meta_test.cc
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fcp/base/meta.h"
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace fcp {
+
+using ::testing::Eq;
+using ::testing::Not;
+
+struct R {
+ virtual ~R() = default;
+ virtual bool Virt1(int i) = 0;
+ virtual bool Virt2(int i, int j) = 0;
+ virtual void Virt3() = 0;
+ int NonVirt1() { return 1; }
+ int NonVirt2() { return 2; }
+ char field;
+};
+
+struct S {
+ virtual ~S() = default;
+ virtual bool Virt1(int i) = 0;
+ virtual bool Virt2(int i, int j) = 0;
+ virtual void Virt3() = 0;
+ int NonVirt1() { return 1; }
+ int NonVirt2() { return 2; }
+ char field;
+};
+
+//
+// Compile-time tests for MemberPointerTraits
+//
+#define STATIC_ASSERT_TARGET_TYPE(member, type) \
+ static_assert( \
+ std::is_same<MemberPointerTraits<decltype(&S::member)>::TargetType, \
+ type>::value, \
+ "Incorrect target type from MemberPointerTraits");
+
+// For some reason the linter otherwise thinks e.g. 'bool(int)' is an old-style
+// cast.
+template<typename T>
+struct Id { using Type = T; };
+
+STATIC_ASSERT_TARGET_TYPE(Virt1, Id<bool(int)>::Type);
+STATIC_ASSERT_TARGET_TYPE(Virt2, Id<bool(int, int)>::Type);
+STATIC_ASSERT_TARGET_TYPE(Virt3, Id<void()>::Type);
+STATIC_ASSERT_TARGET_TYPE(NonVirt1, Id<int()>::Type);
+STATIC_ASSERT_TARGET_TYPE(NonVirt2, Id<int()>::Type);
+STATIC_ASSERT_TARGET_TYPE(field, Id<char>::Type);
+//
+// End compile-time tests for MemberPointerTraits
+//
+
+template<typename T>
+struct TypeIdHolder {
+ static constexpr char kTarget = '\0';
+};
+template<typename T>
+constexpr char TypeIdHolder<T>::kTarget;
+
+// This gives us a unique runtime value per unique type - with which its much
+// easier to verify uniqueness (below).
+template<typename T>
+static constexpr char const* TypeId() {
+ return &TypeIdHolder<T>::kTarget;
+}
+
+//
+// LIFT_MEMBER_TO_TYPE
+//
+
+TEST(MetaTest, MemberTagUniqueness) {
+ std::vector<char const*> type_ids = {
+ TypeId<LIFT_MEMBER_TO_TYPE(S, Virt1)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(S, Virt2)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(S, Virt3)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt1)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt2)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(S, field)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, Virt1)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, Virt2)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, Virt3)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt1)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt2)>(),
+ TypeId<LIFT_MEMBER_TO_TYPE(R, field)>(),
+ };
+
+ for (int i = 0; i < type_ids.size(); i++) {
+ for (int j = 0; j < type_ids.size(); j++) {
+ if (i == j) {
+ continue;
+ }
+ EXPECT_THAT(type_ids[i], Not(Eq(type_ids[j])))
+ << "Member tags must be unique";
+ }
+ }
+}
+
+int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt1)) {
+ return 1;
+}
+
+int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt2)) {
+ return 2;
+}
+
+template<typename R, R S::* M>
+int PokeMember() {
+ return PokeMemberCase(LIFT_MEMBER_POINTER_TO_TYPE(M){});
+}
+
+TEST(MetaTest, MemberTagDispatch) {
+ EXPECT_THAT((PokeMember<bool(int), &S::Virt1>()), Eq(1));
+ EXPECT_THAT((PokeMember<bool(int, int), &S::Virt2>()), Eq(2));
+}
+
+//
+// CastContainerElements
+//
+
+struct X {
+ static constexpr int tag() { return 1; }
+};
+
+struct Y {
+ static constexpr int tag() { return 2; }
+};
+
+template<typename T>
+struct TypedVal {
+ int value;
+
+ bool operator==(TypedVal<T> const& other) const {
+ return other.value == value;
+ }
+ bool operator!=(TypedVal<T> const& other) const { return !(*this == other); }
+};
+
+struct UntypedVal {
+ int tag;
+ int value;
+};
+
+struct CastValByTag {
+ template <typename T>
+ using TargetType = std::optional<TypedVal<T>>;
+
+ template <typename T>
+ TargetType<T> Cast(UntypedVal const& val) const {
+ if (val.tag == T::tag()) {
+ return TypedVal<T>{val.value};
+ } else {
+ return std::nullopt;
+ }
+ }
+};
+
+TEST(MetaTest, CastContainerElements_AllSuccess) {
+ std::vector<UntypedVal> v{
+ {X::tag(), 123},
+ {Y::tag(), 456},
+ {X::tag(), 789}
+ };
+
+ auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
+ auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
+ absl::make_optional(TypedVal<Y>{456}),
+ absl::make_optional(TypedVal<X>{789}));
+
+ EXPECT_THAT(actual, Eq(expected));
+}
+
+TEST(MetaTest, CastContainerElements_AllSuccess_Pack) {
+ std::vector<UntypedVal> v{
+ {X::tag(), 123},
+ {Y::tag(), 456},
+ {X::tag(), 789}
+ };
+
+ // This uses the Pack<> overload instead.
+ auto actual = CastContainerElements(Pack<X, Y, X>{}, v, CastValByTag{});
+ auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
+ absl::make_optional(TypedVal<Y>{456}),
+ absl::make_optional(TypedVal<X>{789}));
+
+ EXPECT_THAT(actual, Eq(expected));
+}
+
+TEST(MetaTest, CastContainerElements_OneFails) {
+ std::vector<UntypedVal> v{
+ {X::tag(), 123},
+ {X::tag(), 456},
+ {X::tag(), 789}
+ };
+
+ // Second element does not have the tag for Y.
+ auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
+ auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
+ std::optional<TypedVal<Y>>(std::nullopt),
+ absl::make_optional(TypedVal<X>{789}));
+
+ EXPECT_THAT(actual, Eq(expected));
+}
+
+//
+// MAKE_LINK and LinkedType<>
+//
+
+namespace links {
+
+namespace a {
+
+struct A1 {};
+struct A2 {};
+struct A3 {};
+
+MAKE_LINK(A1, A2);
+
+} // namespace a
+
+namespace b {
+
+struct B1 {};
+
+MAKE_LINK(B1, a::A3);
+
+} // namespace b
+
+} // namespace links
+
+static_assert(std::is_same<LinkedType<links::a::A1>, links::a::A2>::value,
+ "A1 -> A2");
+static_assert(HasLinkedType<links::a::A1>(), "A1 -> A2");
+static_assert(std::is_same<LinkedTypeOrVoid<links::a::A2>, void>::value,
+ "A2 -/>");
+static_assert(!HasLinkedType<links::a::A2>(), "A2 -/>");
+static_assert(std::is_same<LinkedTypeOrVoid<links::a::A3>, void>::value,
+ "A3 -/>");
+static_assert(!HasLinkedType<links::a::A3>(), "A3 -/>");
+static_assert(std::is_same<LinkedType<links::b::B1>, links::a::A3>::value,
+ "b::B1 -> a::A3");
+static_assert(HasLinkedType<links::b::B1>(), "b::B1 -> a::A3");
+
+//
+// Pack<>
+//
+
+template<typename A1, typename A2, size_t I1, size_t I2>
+constexpr Unit CheckUnpack() {
+ static_assert(std::is_same<A1, X>::value, "A1 == X");
+ static_assert(std::is_same<A2, Y>::value, "A2 == Y");
+ static_assert(I1 == 0, "I1 == 0");
+ static_assert(I2 == 1, "I2 == 0");
+ return {};
+}
+
+template<typename... A, size_t... I>
+constexpr Unit UsePack(Pack<A...>, absl::index_sequence<I...>) {
+ return CheckUnpack<A..., I...>();
+}
+
+template<typename... A>
+constexpr Unit MakeAndUsePack() {
+ return UsePack(Pack<A...>{}, Pack<A...>::MakeIndexSequence());
+}
+
+static_assert(MakeAndUsePack<X, Y>().True(), "Pack<>");
+
+//
+// LiftVoidReturn
+//
+
+TEST(MetaTest, LiftVoidReturn_Void) {
+ int counter = 0;
+ std::function<void()> f = [&counter]() { counter++; };
+
+ f();
+ EXPECT_THAT(counter, Eq(1));
+ auto f_wrapped = LiftVoidReturn(f);
+ EXPECT_THAT(f_wrapped(), Eq(Unit{}));
+ EXPECT_THAT(counter, Eq(2));
+}
+
+TEST(MetaTest, LiftVoidReturn_Void_Args) {
+ int counter = 0;
+ std::function<void(int)> f = [&counter](int i) { counter += i; };
+
+ f(10);
+ EXPECT_THAT(counter, Eq(10));
+ auto f_wrapped = LiftVoidReturn(f);
+ EXPECT_THAT(f_wrapped(32), Eq(Unit{}));
+ EXPECT_THAT(counter, Eq(42));
+}
+
+TEST(MetaTest, LiftVoidReturn_NonVoid) {
+ int counter = 0;
+ std::function<int(int)> f = [&counter](int i) {
+ counter += i;
+ return counter;
+ };
+
+ EXPECT_THAT(f(10), Eq(10));
+ EXPECT_THAT(counter, Eq(10));
+ auto f_wrapped = LiftVoidReturn(f);
+ EXPECT_THAT(f_wrapped(32), Eq(42));
+ EXPECT_THAT(counter, Eq(42));
+}
+
+TEST(MetaTest, LiftVoidReturn_Mutable) {
+ int r = -1;
+ auto f = [&r, counter = 0]() mutable {
+ counter++;
+ r = counter;
+ };
+
+ f();
+ EXPECT_THAT(r, Eq(1));
+ auto f_wrapped = LiftVoidReturn(f);
+ EXPECT_THAT(f_wrapped(), Eq(Unit{}));
+ EXPECT_THAT(r, Eq(2));
+}
+
+TEST(MetaTest, LiftVoidReturn_MutableAndMoveOnly) {
+ int r = -1;
+ auto f = [&r, counter = std::make_unique<int>(0)]() mutable {
+ (*counter)++;
+ r = *counter;
+ };
+
+ f();
+ EXPECT_THAT(r, Eq(1));
+ auto f_wrapped = LiftVoidReturn(std::move(f));
+ EXPECT_THAT(f_wrapped(), Eq(Unit{}));
+ EXPECT_THAT(r, Eq(2));
+}
+
+//
+// FunctionTraits
+//
+
+#define STATIC_ASSERT_FUNCTION_TRAITS(fn, r, ...) \
+ static_assert(std::is_same<FunctionTraits<fn>::ResultType, r>::value, \
+ "Incorrect result type from FunctionTraits"); \
+ static_assert( \
+ std::is_same<FunctionTraits<fn>::ArgPackType, Pack<__VA_ARGS__>>::value, \
+ "Incorrect arg pack from FunctionTraits")
+
+STATIC_ASSERT_FUNCTION_TRAITS(void(), void);
+STATIC_ASSERT_FUNCTION_TRAITS(void(int, char), void, int, char);
+STATIC_ASSERT_FUNCTION_TRAITS(Identity<bool(char const*, int)>, bool,
+ char const*, int);
+
+TEST(MetaTest, IsTypeOneOf) {
+ static_assert(IsTypeOneOf<int, int>());
+ static_assert(IsTypeOneOf<int, int, double>());
+ static_assert(IsTypeOneOf<int, double, int>());
+ static_assert(!IsTypeOneOf<int, bool>());
+ static_assert(!IsTypeOneOf<int, double, char>());
+}
+
+TEST(MetaTest, IsSubsetOf) {
+ using T1 = Pack<int, double>;
+ using T2 = Pack<double, int>;
+ using T3 = Pack<int, double, char>;
+
+ static_assert(IsSubsetOf<T1, T1>::value);
+ static_assert(IsSubsetOf<T1, T2>::value);
+ static_assert(IsSubsetOf<T2, T1>::value);
+ static_assert(IsSubsetOf<T2, T3>::value);
+ static_assert(!IsSubsetOf<T3, T2>::value);
+}
+
+} // namespace fcp