From ea7bf3ae3fc4c10cedd160a11b6ad524d7a4e10c Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Wed, 3 Jan 2018 10:28:21 -0500 Subject: [PATCH] Add `type_util` meta-programming utilities. --- common/BUILD.bazel | 14 +++ common/test/type_util_test.cc | 120 +++++++++++++++++++++ common/type_util.h | 194 ++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 common/test/type_util_test.cc create mode 100644 common/type_util.h diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 14345191d052..7df2fad0bcc2 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -45,6 +45,7 @@ drake_cc_library( ":symbolic", ":symbolic_decompose", ":type_safe_index", + ":type_util", ":unused", ], # Intentionally excluded items: @@ -347,6 +348,11 @@ drake_cc_library( ], ) +drake_cc_library( + name = "type_util", + hdrs = ["type_util.h"], +) + drake_cc_library( name = "unused", hdrs = ["unused.h"], @@ -882,6 +888,14 @@ drake_cc_googletest( ], ) +drake_cc_googletest( + name = "type_util_test", + deps = [ + ":nice_type_name", + ":type_util", + ], +) + drake_cc_googletest( name = "eigen_autodiff_types_test", deps = [":autodiff"], diff --git a/common/test/type_util_test.cc b/common/test/type_util_test.cc new file mode 100644 index 000000000000..24bf68f223f7 --- /dev/null +++ b/common/test/type_util_test.cc @@ -0,0 +1,120 @@ +#include "drake/common/type_util.h" + +#include +#include + +#include + +#include "drake/common/nice_type_name.h" + +using std::string; +using std::vector; + +namespace drake { +namespace { + +template +struct SimpleTemplate {}; + +using Pack = type_pack; + +// Mostly, this just checks for compilation failures. +GTEST_TEST(TypeUtilTest, TypeAt) { + using T_0 = type_at<0, int, double, char, void>::type; + EXPECT_TRUE((std::is_same::value)); + using T_1 = type_at<1, int, double, char, void>::type; + EXPECT_TRUE((std::is_same::value)); + using T_2 = type_at<2, int, double, char, void>::type; + EXPECT_TRUE((std::is_same::value)); + using T_3 = type_at<3, int, double, char, void>::type; + EXPECT_TRUE((std::is_same::value)); + + EXPECT_TRUE((std::is_same, int>::value)); + EXPECT_TRUE((std::is_same, double>::value)); + EXPECT_TRUE((std::is_same, char>::value)); + EXPECT_TRUE((std::is_same, void>::value)); +} + +GTEST_TEST(TypeUtilTest, TypeTags) { + // Ensure that we can default-construct tags for types that are not + // default-constructible. + auto tag_check = type_tag{}; + EXPECT_TRUE((std::is_same< + decltype(tag_check), type_tag>::value)); + auto pack_check_empty = type_pack<>{}; + EXPECT_TRUE((std::is_same< + decltype(pack_check_empty), type_pack<>>::value)); + auto pack_check = type_pack{}; + EXPECT_TRUE((std::is_same< + decltype(pack_check), type_pack>::value)); +} + +GTEST_TEST(TypeUtilTest, Bind) { + using T_0 = Pack::bind; + EXPECT_TRUE((std::is_same< + T_0, SimpleTemplate>::value)); + Pack pack; + using T_1 = decltype(type_bind(pack)); + EXPECT_TRUE((std::is_same< + T_1, SimpleTemplate>::value)); +} + +GTEST_TEST(TypeUtilTest, Extract) { + using T = SimpleTemplate; + using TPack = type_pack_extract; + EXPECT_TRUE((std::is_same::value)); +} + +GTEST_TEST(TypeUtilTest, Visit) { + using PackTags = type_pack< + type_tag, type_tag, type_tag, type_tag>; + vector names; + const vector names_expected = {"int", "double", "char", "void"}; + + auto visitor = [&names](auto tag) { + using T = typename decltype(tag)::type; + names.push_back(NiceTypeName::Get()); + }; + names.clear(); + type_visit(visitor, PackTags{}); + EXPECT_EQ(names, names_expected); + + names.clear(); + type_visit>(visitor, Pack{}); + EXPECT_EQ(names, names_expected); + + // N.B. `Check` will operate types contained within the pack as dictated by + // the `VisitWith` parameter in `type_visit`. + // As an example, see below that the two visit calls will be visiting the + // same types (with the same results), but `Check` will operate on + // (a) the direct types with `visit_with_tag<>` and (b) the tag types with + // `visit_with_default` (the default parameter). + const vector names_expected_sub = {"int", "char", "void"}; + + names.clear(); + type_visit>( + visitor, Pack{}, check_different_from{}); + EXPECT_EQ(names, names_expected_sub); + + names.clear(); + type_visit(visitor, PackTags{}, check_different_from>{}); + EXPECT_EQ(names, names_expected_sub); +}; + +GTEST_TEST(TypeUtilTest, Transform) { + auto seq = sequence_transform( + constant_add{}, std::make_integer_sequence{}); + EXPECT_TRUE((std::is_same< + decltype(seq), std::integer_sequence>::value)); +} + +GTEST_TEST(TypeUtilTest, Hash) { + using T = int; + using U = int; + using V = double; + EXPECT_EQ(type_hash(), type_hash()); + EXPECT_NE(type_hash(), type_hash()); +} + +} // namespace +} // namespace drake diff --git a/common/type_util.h b/common/type_util.h new file mode 100644 index 000000000000..528f36cc3501 --- /dev/null +++ b/common/type_util.h @@ -0,0 +1,194 @@ +#pragma once + +/// @file +/// Basic meta-programming utilities for types, focused on template parameter +/// packs. + +#include +#include +#include +#include +#include + +namespace drake { + +template +struct type_pack; + +namespace detail { + +// Provides type at given index. +template +struct type_at_impl { + using type = typename type_at_impl::type; +}; + +// Base case. +template +struct type_at_impl { + using type = T; +}; + +// Visits a type given a VisitWith mechanism, templated to permit +// conditional execution. +template +struct type_visit_impl { + template + struct runner { + inline static void run(Visitor&& visitor) { + VisitWith::template run(std::forward(visitor)); + } + }; + template + struct runner { + inline static void run(Visitor&&) {} + }; +}; + +// Catches non-template types explicitly. +template +struct type_pack_extract_impl { + // Defer to show that this is a bad instantiation. + static_assert(!std::is_same::value, "Wrong template"); +}; + +template