我试图从我的实际项目中删除尽可能多的代码。这是重现此错误所需的最少代码。似乎编译器需要更多的空间,更多的类型被添加到基本类型列表中。
这是为什么?以及 如何解决此限制的任何想法?
该代码从其他类型列表和类型构造一个扁平类型列表,并删除重复项以为列表中注册的每种类型提供唯一的 id。
我需要此代码来处理类型列表中至少 30-50 种类型。代码的目的是为显式列出的类型提供运行时类型反射系统,如下例所示:
#include <tuple>
// Utils
// Type for concatenated tuples
template<typename... input_t> using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...));
// Helper to check if a type is contained in a tuple already
template <typename T, typename Tuple> struct contains;
template <typename T, typename... Us> struct contains<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {};
template< class T, class U > inline constexpr bool contains_v = contains<T, U>::value;
// Filter Code:
template <class Out, class In> struct filter_duplicates;
// Partial spezialization for the recursive deduction to end (when input tuple is empty)
template <class Out> struct filter_duplicates<Out /*finished filtered output tuple*/, std::tuple<> /*empty input tuple*/>
{
using filtered_tuple_t = Out;
// The filter function deduction ends here if the input tuple is empty (every element checked). In this case _In is the found filtered output tuple
static constexpr Out& filter(Out&& _In) { return _In; }
};
/* Filter template that is used as long the input tuple is not empty.
It builds the output tuple by recursively checking every element in the
input tuple and deciding whether is can be added to the output tuple and
then continuous deduction with the next input element until the
filter_duplicates definition above matches. */
template <class... OutTypes, class InTest, class... InRest>
struct filter_duplicates<std::tuple<OutTypes...>, std::tuple<InTest, InRest...>>
{
// contained is true if the InTest element from the input tuple is contained in the output tuple already
static constexpr bool contained = contains_v<InTest, std::tuple<OutTypes...>>;
// depending on the condition above either add_t or rem_t is used (which adds InTest to the output tuple or not
using add_t = filter_duplicates<std::tuple<OutTypes..., InTest>, std::tuple<InRest...>>;
using rem_t = filter_duplicates<std::tuple<OutTypes... >, std::tuple<InRest...>>;
// These types resolve to a tuple<...> with either added or remove InTest type
using add_tuple_t = typename add_t::filtered_tuple_t;
using rem_tuple_t = typename rem_t::filtered_tuple_t;
// This type is the result of the check if InTest is contained, so its either add_tuple_t or rem_tuple_t
using filtered_tuple_t = std::conditional_t<contained, rem_tuple_t, add_tuple_t>;
// This type is the result of the check if InTest is contained, so its either the filter_duplicate type containing InTest in the OutTypes or not
using filter_t = std::conditional_t<contained, rem_t, add_t>;
// The function takes the unfiltered tuple instance and returns the filtered tuple instance (duplicate tuple entries are filtered)
static constexpr auto filter(std::tuple<OutTypes..., InTest, InRest...>&& _In)
{
return filter_seq<contained>(std::make_index_sequence<sizeof...(OutTypes)>{}, std::make_index_sequence<sizeof...(InRest) + 1 - contained>{}, std::move(_In));
}
// The input tuple for the next deduction step is built by getting all tuple elements except "InTest" in the case it is in the output list already
template<size_t _Skip, size_t... TIndicesOut, size_t... TIndicesIn, class... In>
static constexpr auto filter_seq(std::index_sequence<TIndicesOut...>, std::index_sequence<TIndicesIn...>, std::tuple<In...>&& _In)
{
return filter_t::filter(std::make_tuple(std::move(std::get<TIndicesOut>(_In))..., std::move(std::get<sizeof...(TIndicesOut) + _Skip + TIndicesIn>(_In))...));
}
};
// Some declarations for easier use
template <class T> using no_duplicates_tuple_t = typename filter_duplicates<std::tuple<>, T>::filtered_tuple_t;
template <class T> using no_duplicates_filter = filter_duplicates<std::tuple<>, T>;
// Function to return a filtered tuple given an unfiltered tuple. It uses the filter type above to construct the new tuple with correct type
template<class... In> constexpr auto make_tuple_no_duplicates(std::tuple<In...>&& _In)
{
return no_duplicates_filter<std::tuple<In...>>::filter(std::move(_In));
}
// Type info wrapper (In my project it contains functions like construct or copy and much more for the runtime type reflection)
struct IType {};
struct STypeUnknown : IType { using TType = void; };
template<typename T> struct SType : IType { using TType = T; };
// STypeList forward declation
template <typename...> struct STypeList;
// This type unwrappes a given STypeList into a flattened plain tuple, so it can be concatenated with other STypeLists or Types
// In case the given type is just a normal type, it is converted into a tuple with a single element: tuple<T> (so tuple_cat() can be used later)
template<typename T> struct SUnwrapTypeList
{
using tuple_type = std::tuple<T>;
tuple_type as_tuple;
SUnwrapTypeList(T& _Value) : as_tuple{ _Value } {}
};
// In case the Type is a STypeList its filtered and flattened tuple is used
template<typename... TypeDefs> struct SUnwrapTypeList<STypeList<TypeDefs...>>
{
using TypeList = STypeList<TypeDefs...>;
using tuple_type = typename TypeList::TTupleDef;
tuple_type as_tuple;
SUnwrapTypeList(TypeList& _Value) : as_tuple(_Value.infos) {}
};
// The actual STypeList that can be constructed from other STypeList instances
template<typename... TypeDefs> struct STypeList
{
// Defines the actual underlying flattened and filtered tuple<...>
using TTupleDef = no_duplicates_tuple_t<tuple_cat_t<typename SUnwrapTypeList<TypeDefs>::tuple_type...>>;
// Type count after flattening and filtering
static constexpr size_t TypeCount() { return std::tuple_size<TTupleDef>::value; }
// helper to get an index_sequence for the filtered flattened tuple
static constexpr auto type_seq = std::make_index_sequence<TypeCount()>{};
// All type infos given via the STypeList constructor, flattened and filtered
TTupleDef infos;
// This constructor is used in the example below. It can take arbitrary STypeLists or SType<XXX> lists and flattens and filteres them using the code above
STypeList(TypeDefs... _Types) noexcept : STypeList(type_seq, std::tuple_cat(SUnwrapTypeList<TypeDefs>(_Types).as_tuple...)) {}
STypeList(TTupleDef& _Types) noexcept : STypeList(type_seq, _Types) {}
STypeList(STypeList&& _Move) noexcept : STypeList(type_seq, _Move.infos) {}
STypeList(const STypeList& _Copy) noexcept : STypeList(type_seq, _Copy.infos) {}
private:
// Final constructor initializing infos. TListIndices is the index_sequence for the filtered and flattened tuple, while TType is the unfiltered flattened tuple from the constructors above
template <size_t... TListIndices, typename... TTypes>
STypeList(std::index_sequence<TListIndices...>, std::tuple<TTypes...>&& _Types) noexcept
: infos{ make_tuple_no_duplicates(std::move(_Types)) }
{ }
// Final constructor initializing infos via copy
template <size_t... TListIndices>
STypeList(std::index_sequence<TListIndices...>, const TTupleDef& _Infos) noexcept
: infos(_Infos)
{ }
};
// Test Code:
struct STestType1 { };
struct STestType2 { };
static inline auto TypeListBase = STypeList
(
STypeUnknown()
,SType<bool>()
,SType<float>()
,SType<double>()
,SType<int>()
,SType<long>()
,SType<char>() //<- comment in to produce error
);
static inline auto TypeListA = STypeList
(
TypeListBase
,SType<STestType1>()
);
static inline auto TypeListB = STypeList
(
TypeListBase
,SType<STestType2>()
);
static inline auto TypeListAB = STypeList
(
TypeListA,
TypeListB
);
int main()
{
}
尝试编译时输出将是(使用 Visual Studio 2017,gcc 和 VS2019 中似乎也出现了问题):
1>------ Build started: Project: VsTest, Configuration: Debug x64 ------
1>VsTest.cpp
1>XXX\vstest.cpp(20): fatal error C1060: compiler is out of heap space
1>Done building project "VsTest.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========