Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some new type traits that will be used for expressions. #477

Merged
merged 3 commits into from
Sep 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
117 changes: 108 additions & 9 deletions src/util/TypeTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,126 @@
// Type traits for template metaprogramming

#pragma once
#include <tuple>
#include <utility>
#include <variant>

namespace ad_utility {

namespace detail {
// Check (via the compiler's template matching mechanism) whether a given type
// is an instantiation of a given template.
// Example 1: IsInstantiationOf<std::vector>::Instantiation<std::vector<int>>
// is true.
// Example 2: IsInstantiationOf<std::vector>::Instantiation<std:map<int, int>>
// is false
template <template <typename...> typename Template>
struct IsInstantiationOfImpl {
struct IsInstantiationOf {
template <typename T>
struct Inner : std::false_type {};
struct Instantiation : std::false_type {};

template <typename... Ts>
struct Inner<Template<Ts...>> : std::true_type {};
struct Instantiation<Template<Ts...>> : std::true_type {};
};

// Given a templated type (e.g. std::variant<A, B, C>), provide a type where the
// inner types are "lifted" by a given outer type.
// Example: LiftInnerTypes<std::variant,
// std::vector>::TypeToLift<std::variant<A, B, C>>::LiftedType is
// std::variant<std::vector<A>, std::vector<B>, std::vector<C>>
template <template <typename...> typename Template,
template <typename> typename TypeLifter>
struct LiftInnerTypes {
template <typename>
struct TypeToLift {};

template <typename... InnerTypes>
struct TypeToLift<Template<InnerTypes...>> {
using LiftedType = Template<TypeLifter<InnerTypes>...>;
};
};

// TupleToVariantImpl<std::tuple<A, B, ...>>::type = std::variant<A, B, C>
template <typename T>
struct TupleToVariantImpl {};

template <typename... Ts>
struct TupleToVariantImpl<std::tuple<Ts...>> {
using type = std::variant<Ts...>;
};

} // namespace detail

/// IsInstantiation<someTemplate, someType>::value is true iff `someType` is an
/// instantiation of the template `someTemplate`. This can be used to define
/// concepts, see below
/// isInstantiation<SomeTemplate, SomeType> is true iff `SomeType` is an
/// instantiation of the template `SomeTemplate`. This can be used to define
/// concepts, see below.
template <template <typename...> typename Template, typename T>
using IsInstantiation =
typename detail::IsInstantiationOfImpl<Template>::template Inner<T>;
constexpr static bool isInstantiation =
detail::IsInstantiationOf<Template>::template Instantiation<T>::value;

/// isVector<T> is true if and only if T is an instantiation of std::vector
template <typename T>
constexpr static bool isVector = IsInstantiation<std::vector, T>::value;
constexpr static bool isVector = isInstantiation<std::vector, T>;

/// isTuple<T> is true if and only if T is an instantiation of std::tuple
template <typename T>
constexpr static bool isTuple = isInstantiation<std::tuple, T>;

/// isVariant<T> is true if and only if T is an instantiation of std::variant
template <typename T>
constexpr static bool isVariant = isInstantiation<std::variant, T>;

/// Two types are similar, if they are the same when we remove all cv (const or
/// volatile) qualifiers and all references
template <typename T, typename U>
constexpr static bool isSimilar =
std::is_same_v<std::decay_t<T>, std::decay_t<U>>;

/// isTypeContainedIn<T, U> It is true iff type U is a pair, tuple or variant
/// and T `isSimilar` (see above) to one of the types contained in the tuple,
/// pair or variant.
template <typename... Ts>
constexpr static bool isTypeContainedIn = false;

template <typename T, typename... Ts>
constexpr static bool isTypeContainedIn<T, std::tuple<Ts...>> =
(... || isSimilar<T, Ts>);

template <typename T, typename... Ts>
constexpr static bool isTypeContainedIn<T, std::variant<Ts...>> =
(... || isSimilar<T, Ts>);

template <typename T, typename... Ts>
constexpr static bool isTypeContainedIn<T, std::pair<Ts...>> =
(... || isSimilar<T, Ts>);

/// A templated bool that is always false, independent of the template parameter
/// T.
template <typename T>
constexpr static bool alwaysFalse = false;

/// From the type Tuple (std::tuple<A, B, C....>) creates the type
/// std::tuple<TypeLifter<A>, TypeLifter<B>,...>
template <typename Tuple, template <typename> typename TypeLifter>
requires isTuple<Tuple> using LiftedTuple = typename detail::LiftInnerTypes<
std::tuple, TypeLifter>::template TypeToLift<Tuple>::LiftedType;

/// From the type Variant (std::variant<A, B, C....>) creates the type
/// std::variant<TypeLifter<A>, TypeLifter<B>,...>
template <typename Variant, template <typename> typename TypeLifter>
requires isVariant<Variant> using LiftedVariant =
typename detail::LiftInnerTypes<
std::variant, TypeLifter>::template TypeToLift<Variant>::LiftedType;

/// From the type std::tuple<A, B, ...> makes the type std::variant<A, B, ...>
template <typename Tuple>
requires isTuple<Tuple> using TupleToVariant =
typename detail::TupleToVariantImpl<Tuple>::type;

/// From the types X = std::tuple<A, ... , B>, , Y = std::tuple<C, ..., D>...
/// makes the type TupleCat<X, Y> = std::tuple<A, ..., B, C, ..., D, ...> (works
/// for an arbitrary number of tuples as template parameters.
template <typename... Tuples>
requires(...&& isTuple<Tuples>) using TupleCat =
decltype(std::tuple_cat(std::declval<Tuples&>()...));
} // namespace ad_utility
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,7 @@ add_test(ZstdCompressionTest ZstdCompressionTest )
add_executable(TaskQueueTest TaskQueueTest.cpp)
target_link_libraries(TaskQueueTest gtest_main ${CMAKE_THREAD_LIBS_INIT})
add_test(TaskQueueTest TaskQueueTest)

add_executable(TypeTraitsTest TypeTraitsTest.cpp)
add_test(TypeTraitsTest TypeTraitsTest)
target_link_libraries(TypeTraitsTest gtest_main ${CMAKE_THREAD_LIBS_INIT})
102 changes: 102 additions & 0 deletions test/TypeTraitsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2021, University of Freiburg, Chair of Algorithms and Data
// Structures. Author: Johannes Kalmbach <kalmbacj@cs.uni-freiburg.de>

#include <gtest/gtest.h>

#include "../src/util/TypeTraits.h"

using namespace ad_utility;
TEST(TypeTraits, IsSimilar) {
static_assert(isSimilar<int, const int&>);
static_assert(isSimilar<int&, const int&>);
static_assert(isSimilar<volatile int&, const int>);
static_assert(!isSimilar<volatile int&, const int*>);
ASSERT_TRUE(true);
}

TEST(TypeTraits, IsContained) {
using tup = std::tuple<int, char>;
using nested = std::tuple<tup>;
static_assert(isTypeContainedIn<int, tup>);
static_assert(isTypeContainedIn<char, tup>);
static_assert(isTypeContainedIn<tup, nested>);
static_assert(!isTypeContainedIn<tup, char>);
static_assert(!isTypeContainedIn<unsigned int, tup>);
static_assert(!isTypeContainedIn<int, int>);
ASSERT_TRUE(true);
}

TEST(TypeTraits, IsInstantiation) {
static_assert(isVector<std::vector<int>>);
static_assert(!isVector<std::tuple<int>>);
static_assert(!isVector<int>);

static_assert(isTuple<std::tuple<int>>);
static_assert(isTuple<std::tuple<int, bool>>);
static_assert(!isTuple<std::variant<int, bool>>);
static_assert(!isTuple<int>);

static_assert(isVariant<std::variant<int>>);
static_assert(isVariant<std::variant<int, bool>>);
static_assert(!isVariant<std::tuple<int, bool>>);
static_assert(!isVariant<int>);
ASSERT_TRUE(true);
}

template <typename>
struct TypeLifter {};

TEST(TypeTraits, Lift) {
using T1 = std::tuple<int>;
using LT = LiftedTuple<T1, TypeLifter>;
static_assert(std::is_same_v<std::tuple<TypeLifter<int>>, LT>);

using V = std::variant<int>;
using LV = LiftedVariant<V, TypeLifter>;
static_assert(std::is_same_v<std::variant<TypeLifter<int>>, LV>);

using TT = std::tuple<int, bool, short>;
using LTT = LiftedTuple<TT, TypeLifter>;
static_assert(
std::is_same_v<
std::tuple<TypeLifter<int>, TypeLifter<bool>, TypeLifter<short>>,
LTT>);

using VV = std::variant<int, bool, short>;
using LVV = LiftedVariant<VV, TypeLifter>;
static_assert(
std::is_same_v<
std::variant<TypeLifter<int>, TypeLifter<bool>, TypeLifter<short>>,
LVV>);
ASSERT_TRUE(true);
}

TEST(TypeTraits, TupleToVariant) {
using T = std::tuple<int>;
using V = TupleToVariant<T>;
static_assert(std::is_same_v<V, std::variant<int>>);

using TT = std::tuple<int, short, bool>;
using VV = TupleToVariant<TT>;
static_assert(std::is_same_v<VV, std::variant<int, short, bool>>);
ASSERT_TRUE(true);
}

TEST(TypeTraits, TupleCat) {
using T1 = std::tuple<int, short>;
using T2 = std::tuple<bool, long, size_t>;
using T3 = std::tuple<>;

static_assert(std::is_same_v<T1, TupleCat<T1>>);
static_assert(std::is_same_v<T2, TupleCat<T2>>);
static_assert(std::is_same_v<T3, TupleCat<T3>>);

static_assert(std::is_same_v<T1, TupleCat<T1, T3>>);
static_assert(std::is_same_v<T2, TupleCat<T2, T3>>);

static_assert(std::is_same_v<std::tuple<int, short, bool, long, size_t>,
TupleCat<T1, T2>>);
static_assert(std::is_same_v<std::tuple<int, short, bool, long, size_t>,
TupleCat<T1, T3, T2>>);
ASSERT_TRUE(true);
}