Skip to content

Commit

Permalink
Reduce the symbol load caused by feature templates (#41)
Browse files Browse the repository at this point in the history
* Remove the use of the aggregator

* Use flat and lean inheritance structure

* Remove all unnecessary template instantiations

* Remove unnecessary code

* Fix code check

* Add a unit test for FlattenFeatures

* Make a template easier for MSVC

Co-authored-by: Michael X. Grey <[email protected]>
Co-authored-by: Grey <[email protected]>
Co-authored-by: Steven Peters <[email protected]>
  • Loading branch information
4 people authored May 7, 2020
1 parent e228b67 commit 6e96306
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 28 deletions.
1 change: 0 additions & 1 deletion dartsim/src/plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ struct DartsimFeatures : FeatureList<
> { };

class Plugin :
public virtual Implements3d<DartsimFeatures>,
public virtual Base,
public virtual CustomFeatures,
public virtual EntityManagementFeatures,
Expand Down
224 changes: 200 additions & 24 deletions include/ignition/physics/detail/FeatureList.hh
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ namespace ignition
class ExtractFeatures
: public VerifyFeatures<F>
{
public: using Result = std::tuple<F>;
public: using type = std::tuple<F>;
};

/// \private This specialization of ExtractFeatures is used to wipe away
Expand All @@ -182,7 +182,7 @@ namespace ignition
class ExtractFeatures<std::tuple<F...>>
: public VerifyFeatures<F...>
{
public: using Result = std::tuple<F...>;
public: using type = std::tuple<F...>;
};

/// \private This specialization of ExtractFeatures is used to wipe away
Expand All @@ -195,7 +195,7 @@ namespace ignition
void_t<typename SomeFeatureList::Features>>
: public VerifyFeatures<typename SomeFeatureList::Features>
{
public: using Result = typename SomeFeatureList::Features;
public: using type = typename SomeFeatureList::Features;
};

/// \private This specialization skips over any void entries. This allows
Expand All @@ -204,22 +204,35 @@ namespace ignition
template <>
class ExtractFeatures<void>
{
public: using Result = std::tuple<>;
public: using type = std::tuple<>;
};

/////////////////////////////////////////////////
template <typename DiscardTuple, typename InputTuple>
/// \private This struct wraps the TupleContainsBase class to create a
/// tuple filter that can be passed to FilterTuple.
template <typename DiscardTuple>
struct RedundantTupleFilter
{
template <typename T>
struct Apply : TupleContainsBase<T, DiscardTuple> { };
};

/////////////////////////////////////////////////
template <template <typename> class Filter, typename InputTuple>
struct FilterTuple;

/////////////////////////////////////////////////
template <typename DiscardTuple, typename... InputTypes>
struct FilterTuple<DiscardTuple, std::tuple<InputTypes...>>
/// \private This class will apply a Filter to a tuple and produce a new
/// tuple that only includes the tuple elements which were ignored by the
/// Filter.
template <template <typename> class Filter, typename... InputTypes>
struct FilterTuple<Filter, std::tuple<InputTypes...>>
{
using Result = decltype(std::tuple_cat(
using type = decltype(std::tuple_cat(
std::conditional_t<
// If the input type is a base class of anything that should be
// discared ...
TupleContainsBase<InputTypes, DiscardTuple>::value,
Filter<InputTypes>::value,
// ... then we should leave it out of the final tuple ...
std::tuple<>,
// ... otherwise, include it.
Expand All @@ -228,6 +241,129 @@ namespace ignition
>()...));
};

/////////////////////////////////////////////////
/// \private Use this struct to remove the tuple elements of one tuple
/// from another tuple
template <typename DiscardTuple>
struct SubtractTuple
{
template <typename T>
using Filter =
typename RedundantTupleFilter<DiscardTuple>::template Apply<T>;

/// This struct will contain a nested struct called `type` which will be
/// a tuple with all the elements of FromTuple that are not present in
/// DiscardTuple
template <typename FromTuple>
struct From : FilterTuple<Filter, FromTuple> { };
};

/////////////////////////////////////////////////
/// \private This template takes in a tuple as InputTuple and gives back
/// a tuple where every element type is only present once.
template <typename InputTuple>
struct RemoveTupleRedundancies;

template <typename T>
struct wrap { };

template <typename Tuple>
struct unwrap;

template <typename... T>
struct unwrap<std::tuple<wrap<T>...>>
{
using type = std::tuple<T...>;
};

template <typename... InputTupleArgs>
struct RemoveTupleRedundancies<std::tuple<InputTupleArgs...>>
{
template <typename PartialResultInput, typename...>
struct Impl;

template <typename PartialResultInput>
struct Impl<PartialResultInput>
{
using type = std::tuple<>;
};

template <typename ParentResultInput, typename F1, typename... Others>
struct Impl<ParentResultInput, F1, Others...>
{
using PartialResult =
std::conditional_t<
TupleContainsBase<wrap<F1>, ParentResultInput>::value,
std::tuple<>,
std::tuple<wrap<F1>>
>;

using AggregateResult = decltype(std::tuple_cat(
ParentResultInput(), PartialResult()));

using type = decltype(std::tuple_cat(
PartialResult(),
typename Impl<AggregateResult, Others...>::type()));
};

using type =
typename unwrap<
typename Impl<std::tuple<>, InputTupleArgs...>::type
>::type;
};

/////////////////////////////////////////////////
/// \private This template is used to take a hierarchy of FeatureLists and
/// flatten them into a single tuple.
template <typename FeatureTuple, typename = void_t<>>
struct FlattenFeatures;

/////////////////////////////////////////////////
/// \private This template is a helper for FlattenFeatures
template <typename FeatureOrList, typename = void_t<>>
struct ExpandFeatures
{
using type = std::conditional_t<
std::is_void_v<typename FeatureOrList::RequiredFeatures>,
std::tuple<FeatureOrList>,
decltype(std::tuple_cat(
std::tuple<FeatureOrList>(),
typename FlattenFeatures<
typename FeatureOrList::RequiredFeatures>::type()))
>;
};

/////////////////////////////////////////////////
template <typename List>
struct ExpandFeatures<List, void_t<typename List::Features>>
{
using type = typename FlattenFeatures<typename List::Features>::type;
};

/////////////////////////////////////////////////
template <typename FeatureListT>
struct FlattenFeatures<
FeatureListT, void_t<typename FeatureListT::FeatureTuple>>
{
using type =
typename FlattenFeatures<typename FeatureListT::FeatureTuple>::type;
};

/////////////////////////////////////////////////
template <typename... Features>
struct FlattenFeatures<std::tuple<Features...>, void_t<>>
{
using type = decltype(std::tuple_cat(
typename ExpandFeatures<Features>::type()...));
};

/////////////////////////////////////////////////
template <>
struct FlattenFeatures<void>
{
using type = std::tuple<>;
};

/////////////////////////////////////////////////
/// \private CombineListsImpl provides the implementation of CombineLists.
/// This helper implementation structure allows us to filter out repeated
Expand All @@ -238,7 +374,7 @@ namespace ignition
template <typename PartialResultInput>
struct CombineListsImpl<PartialResultInput>
{
using Result = std::tuple<>;
using type = std::tuple<>;
};

template <typename ParentResultInput, typename F1, typename... Others>
Expand All @@ -247,28 +383,27 @@ namespace ignition
// Add the features of the feature list F1, while filtering out any
// repeated features.
using InitialResult =
typename FilterTuple<
ParentResultInput,
typename ExtractFeatures<F1>::Result
>::Result;
typename SubtractTuple<ParentResultInput>
::template From<typename ExtractFeatures<F1>::type>::type;

// Add the features that are required by F1, while filtering out any
// repeated features.
using PartialResult = decltype(std::tuple_cat(
InitialResult(),
typename FilterTuple<
decltype(std::tuple_cat(ParentResultInput(), InitialResult())),
typename ExtractFeatures<typename F1::RequiredFeatures>::Result
>::Result()));
typename SubtractTuple<
decltype(std::tuple_cat(ParentResultInput(), InitialResult()))>
::template From<
typename ExtractFeatures<typename F1::RequiredFeatures>::type
>::type()));

// Define the tuple that the child should use to filter its list
using ChildFilter =
decltype(std::tuple_cat(ParentResultInput(), PartialResult()));

// Construct the final result
using Result = decltype(std::tuple_cat(
using type = decltype(std::tuple_cat(
PartialResult(),
typename CombineListsImpl<ChildFilter, Others...>::Result()));
typename CombineListsImpl<ChildFilter, Others...>::type()));
};

/// \private CombineLists is used to take variadic lists of features,
Expand All @@ -280,7 +415,7 @@ namespace ignition
struct CombineLists
{
public: using Result =
typename CombineListsImpl<std::tuple<>, FeatureLists...>::Result;
typename CombineListsImpl<std::tuple<>, FeatureLists...>::type;
};

/////////////////////////////////////////////////
Expand Down Expand Up @@ -367,9 +502,50 @@ namespace ignition
template <template<typename> class Selector, typename FeatureListT>
struct ExtractAPI
{
public: template<typename... T>
class type : public virtual
Aggregate<Selector, FeatureListT>::template type<T...> { };
template <typename FeatureTuple, typename... T>
struct Select;

template <typename... Features, typename... T>
struct Select<std::tuple<Features...>, T...>
{
using type =
typename RemoveTupleRedundancies<
std::tuple<typename Selector<Features>::template type<T...>...>
>::type;
};

template <typename T>
struct Filter : std::is_same<Selector<T>, Empty> { };

template <typename... T>
using Bases =
typename Select<
typename FilterTuple<
Filter,
typename FlattenFeatures<FeatureListT>::type
>::type,
T...
>::type;

template <typename... T>
struct S : std::tuple_size<Bases<T...>> { };

template <typename... T>
using IndexSequence = std::make_index_sequence<S<T...>::value>;

template <typename>
struct Impl;

template <std::size_t... I>
struct Impl<std::index_sequence<I...>>
{
template<typename... T>
class type
: public virtual std::tuple_element<I, Bases<T...>>::type... { };
};

template <typename... T>
using type = typename Impl<IndexSequence<T...>>::template type<T...>;
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/FeatureList_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ TEST(FeatureList_TEST, Hierarchy)
// This test makes sure that a hierarchy of FeatureLists can compile.
// As long as the line below can compile, the test is passed.
HierarchyLevel3();

using Level3Tuple = detail::FlattenFeatures<HierarchyLevel3>::type;
EXPECT_TRUE((detail::TupleContainsBase<FeatureA, Level3Tuple>::value));
EXPECT_TRUE((detail::TupleContainsBase<FeatureB, Level3Tuple>::value));
EXPECT_TRUE((detail::TupleContainsBase<FeatureC, Level3Tuple>::value));
EXPECT_TRUE((detail::TupleContainsBase<Conflict1, Level3Tuple>::value));
EXPECT_TRUE((detail::TupleContainsBase<Conflict2, Level3Tuple>::value));
}

int main(int argc, char **argv)
Expand Down
7 changes: 4 additions & 3 deletions src/FilterTuple_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ class ClassD : public ClassB { };
using Filter = std::tuple<ClassA, ClassD>;
using Input = std::tuple<ClassB, ClassC, ClassD, ClassA>;

using Result = ignition::physics::detail::FilterTuple<Filter, Input>::Result;
using Result = ignition::physics::detail::SubtractTuple<Filter>
::From<Input>::type;

using UnfilteredResult =
ignition::physics::detail::FilterTuple<std::tuple<>, Input>::Result;
ignition::physics::detail::SubtractTuple<std::tuple<>>::From<Input>::type;

TEST(FilterTuple_TEST, FilterTupleResult)
{
Expand All @@ -46,7 +47,7 @@ TEST(FilterTuple_TEST, FilterTupleResult)

using SingleCombineLists =
ignition::physics::detail::CombineListsImpl<
std::tuple<>, ClassA>::Result;
std::tuple<>, ClassA>::type;

using SingleCombineListsInitial =
ignition::physics::detail::CombineListsImpl<
Expand Down

0 comments on commit 6e96306

Please sign in to comment.