Quantcast
Channel: Active questions tagged gcc - Stack Overflow
Viewing all articles
Browse latest Browse all 22006

Compiler out of heap space (gcc and msvc)

$
0
0

I am trying to remove duplicate types from a type list template<typename... Types> struct STypeList. This type list is constructed from other type lists and types like this:

#define DECLARE_TYPE(typ) SType<typ>(#typ)

static inline auto BaseTypeList = STypeList
(
    STypeUnkown(),
    DECLARE_TYPE(f32),
    DECLARE_TYPE(f64),
    DECLARE_TYPE(string)
    //...
);

// Library A
static inline auto GameTypeList = STypeList
(
    BaseTypeList,
    DECLARE_TYPE(EHealthState),
    //...
);

// Library B
static inline auto FrameworkTypeList = STypeList
(
    BaseTypeList,
    DECLARE_TYPE(SRndCamera)
    //...
);

// Executable
static inline auto TypeList = STypeList
(
    FrameworkTypeList,
    GameTypeList,
    DECLARE_TYPE(SApplication)
    //...
);

(I know that I could use typeid and name() to get names, but I want full control and maybe change names to different schemes at some time)

These type lists are defined across different libraries, so I stumbled upon the problem of Library A using the BaseTypeList, adding its own types and Libary B also using the BaseTypeList and adding its own types. In my executable I use both type lists and add them together and also add some types only known to the executable.

The type list is used to provide a type id or type index into the list. This list internally holds a std::tuple of instanciated SType<T> : IType that provide type infos for runtime usage.

My technique to find the index of a given type in the list involves a folding sum that fails if the type is not unique in the list. Thats my motivation to remove duplicates.

This is the template code for the STypeList. It unwraps given TypeLists and flattens them into one plain std::tuple<...> infos (and a pinfos[] array which holds pointers to the tuple members, indexable via a runtime index):

// Base class for data type description
struct IType
{
    constexpr IType() {}
    virtual ~IType() {};
    virtual TTypeSize Size() const = 0;
    //virtual TTypeID TypeID() const = 0;
    virtual string TypeName() const = 0;
    virtual void Copy(void* _Dest, const void* _Source) const = 0;
    virtual void Construct(void* _Dest) const = 0;
    virtual void Destroy(void* _Dest) const = 0;
    virtual string ToString(const void* _pSource) const = 0;
    virtual b8 Equals(const void* _pFirst, const void* _pSecond) const = 0;
    virtual b8 Convert(TTypeID _DestType, void* _Dest, const void* _Source) const = 0;
};

// Contains a list of registered types (forward declaration)
template <typename...> struct STypeList;

// Concatenates tuples
template<typename... input_t> using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...));

// Unwraps types and type lists into tuples
template<typename T> struct SUnwrapTypeList
{
    using tuple_type = tuple<T>;
    tuple_type as_tuple;
    SUnwrapTypeList(T& _Value) : as_tuple{ _Value } {}
};
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) {}
};

// Type list that stores all given types definitions flattened in a tuple
template<typename... TypeDefs> struct STypeList
{
    //using TTupleDef = no_duplicates_tuple<tuple_cat_t<typename SUnwrapTypeList<TypeDefs>::tuple_type...>>;
    using TTupleDef = tuple_cat_t<typename SUnwrapTypeList<TypeDefs>::tuple_type...>;
    static constexpr size_t TypeCount() { return tuple_size<TTupleDef>::value; }

    TTupleDef infos;
    const IType* pinfos[TypeCount()] = { nullptr }; // pointers to the IDataType elements in the tuple

    // Generic mixed TypeList, TypeDefs constructor (e.g. STypeList<...> A, f32 B, STypeList<...> C ...)
    STypeList(TypeDefs... _Types) noexcept
        : STypeList(make_index_sequence<TypeCount()>{}, 
                    std::tuple_cat(SUnwrapTypeList<TypeDefs>(_Types).as_tuple...))
    { }

    // Flattened Tuple Constructor
    STypeList(TTupleDef& _Types) noexcept
        : STypeList(make_index_sequence<TypeCount()>{}, _Types)
    { }

    // Move Constructor
    STypeList(STypeList&& _Move) noexcept
        : STypeList(make_index_sequence<TypeCount()>{}, _Move.infos)
    { }

    // Copy Constructor
    STypeList(const STypeList& _Copy) noexcept
        : STypeList(make_index_sequence<TypeCount()>{}, _Copy.infos)
    { }

private:
    // Final constructor initializing infos and pinfos (pointers to the elements in the tuple)
    template <size_t... TListIndices, typename... TTypes>
    STypeList(index_sequence<TListIndices...>, tuple<TTypes...>&& _Types)
        //: infos{ make_tuple_no_duplicates(_Types) }
        : infos{ _Types }
        , pinfos { &std::get<TListIndices>(infos)... }
    { }

    // Final constructor initializing infos and pinfos (pointers to the elements in the tuple)
    template <size_t... TListIndices, typename... TTypes>
    STypeList(index_sequence<TListIndices...>, const tuple<TTypes...>& _Types)
        //: infos{ make_tuple_no_duplicates(_Types) }
        : infos{ _Types }
        , pinfos{ &std::get<TListIndices>(infos)... }
    { }

public:
    // Returns the count of types registered in this type list
    size_t Count() const { return TypeCount(); }

    // Returns the ID of a registered type in this list
    template<typename T> static constexpr TTypeID GetTypeIDFromType()
    {
        return getTypeIDFromTypeSeq<T>(std::make_index_sequence<TypeCount()>{});
    }

    // Returns the type info of a type registered in this list
    template<typename T> constexpr auto& GetTypeInfoFromType() const
    {
        return std::get<GetTypeIDFromType<T>()>(infos);
    }

    // Returns the type info of a given type id registered in this list. (Make sure the type id was obtained by this list and not any other)
    const IType* GetTypeInfoFromID(TTypeID _TypeID) const
    {
        if(_TypeID >= TypeCount())
            return pinfos[0]; //0 should be default/unkown type
        return pinfos[_TypeID];
    }

private:
    // Retrieve the index of a type in the tuple at compile time
    template<typename T, size_t... TIndices> static constexpr TTypeID getTypeIDFromTypeSeq(const index_sequence<TIndices...>&)
    {
        return (TTypeID)(... + std::get<is_same<remove_const_t<remove_reference_t<T>>, typename tuple_element_t<TIndices, TTupleDef>::TType>::value>(pair{ 0, TIndices }));
    }
};

Now as you can see, there is code commented out in the tuple type and constructor initializer. This is my approach to remove the duplicates that ultimately cause msvc and gcc to run out of heap space. So I assume my template code does not terminate. My lists are not that long ~20-30 entries so I think this should work in principle.

This is my code to remove duplicates, it should be able to instantiate a filtered tuple from an unfiltered one as seen in the STypeList constructor above:

// Filter duplicates
template <class Out, class In>
struct filter_duplicates;
// Use this if input tuple (rest) is empty
template <class Out>
struct filter_duplicates<Out, std::tuple<>>
{
    using type = Out;
    static constexpr Out& filter(Out& _In) { return _In; } // I suspect the problem here, where the compiler should terminate the recursion. Maybe it doesn't find this definition?
};
template <class... OutTypes, class InTest, class... InRest>
struct filter_duplicates<std::tuple<OutTypes...>, std::tuple<InTest, InRest...>>
{
    using type = std::conditional_t<
        contains<InTest, std::tuple<OutTypes...>>::value        // Test if in OutTypes already
        , typename filter_duplicates<std::tuple<OutTypes...        >, std::tuple<InRest...>>::type  // Dont add if contained
        , typename filter_duplicates<std::tuple<OutTypes..., InTest>, std::tuple<InRest...>>::type  // Add to Out if not contained
    >;

    using filter_t = std::conditional_t<
        contains<InTest, std::tuple<OutTypes...>>::value        // Test if in OutTypes already
        , filter_duplicates<std::tuple<OutTypes...        >, std::tuple<InRest...>> // Dont add if contained
        , filter_duplicates<std::tuple<OutTypes..., InTest>, std::tuple<InRest...>> // Add to Out if not contained
    >;

    static constexpr auto filter(std::tuple<OutTypes..., InTest, InRest...> _In)
    {
        constexpr size_t skip = contains<InTest, std::tuple<OutTypes...>>::value;
        return filter_seq<skip>(std::make_index_sequence<sizeof...(OutTypes)>{}, std::make_index_sequence<sizeof...(InRest) + 1 - skip>{}, _In);
    }

    template<size_t _Skip, size_t... TIndicesOut, size_t... TIndicesIn, class... In>
    static constexpr auto filter_seq(index_sequence<TIndicesOut...>, index_sequence<TIndicesIn...>, std::tuple<In...> _In)
    {
        return filter_t::filter(std::make_tuple(std::get<TIndicesOut>(_In)..., std::get<sizeof...(TIndicesOut) + _Skip + TIndicesIn>(_In)...));
    }
};

template <class T> using no_duplicates_tuple = typename filter_duplicates<std::tuple<>, T>::type;
template <class T> using no_duplicates_filter = filter_duplicates<std::tuple<>, T>;

template<class... In> constexpr auto make_tuple_no_duplicates(tuple<In...>&& _In)
{
    return no_duplicates_filter<tuple<In...>>::filter(_In);
}

template<class... In> constexpr auto make_tuple_no_duplicates(const tuple<In...>& _In)
{
    return no_duplicates_filter<tuple<In...>>::filter(_In);
}

This code goes through the input type list one by one and checks if the current output type list contains the type already, and then proceeds with either adding the tested type to the output list or not. The second part of the code builds a function that constructs the output tuple from the unfiltered input tuple by recursively selecting indices from the input tuple. When I use the duplicate removal code with some types (more than 5-6) the msvc and gcc go into infinite loops and stop with internal compiler errors or out of heap errors.

Is there a way to accomplish my goal in a simpler way? Do I have an error in my filter_duplicate code? Is this a compiler bug in both gcc and msvc? What other issues do I might have in my code?

I am not yet too confident using the newer C++17 features, so any help and clarification is much appreciated.

PS.: For completeness SType<T> might be implemented like this (but that shouldn't matter much):

// General implementation
template<typename T>
struct SType : IType
{
    using TType = T;
    const char* s_pTypeName;

    template <size_t N> constexpr SType(TTypeID _ID, const char(&_Name)[N])
    {
        s_pTypeName = _Name;
    }

    static constexpr b8 IS_SERIALIZABLE() { return /*is_packed<TType>::value == 1 ||*/ std::is_enum<TType>::value == 1; }
    virtual TTypeSize Size() const { return sizeof(TType); }
    virtual string TypeName() const { return s_pTypeName; }
    virtual b8 Equals(const void* _pFirst, const void* _pSecond) const { return equalsCheck(_pFirst, _pSecond); }
    virtual void Copy(void* _pDest, const void* _pSource) const { copyCheck(_pDest, _pSource); }
    virtual void Construct(void* _pDest) const { new (reinterpret_cast<TType*>(_pDest)) TType(); }
    virtual void Destroy(void* _pDest) const { (reinterpret_cast<TType*>(_pDest))->~TType(); }
    virtual string ToString(const void* _pSource) const { return "unknown_data"; }
    virtual b8 Convert(TTypeID _DestType, void* _Dest, const void* _Source) const { return STypeConversion::Convert<TType>(_DestType)(_Source, _Dest); };

private:
    template< typename U = TType > void copyCheck(void* _pDest, const void* _pSource, typename std::enable_if<std::is_copy_assignable<U>::value, U>::type* = 0) const { *reinterpret_cast<TType*>(_pDest) = *reinterpret_cast<const TType*>(_pSource); }
    template< typename U = TType > void copyCheck(void* _pDest, const void* _pSource, typename std::enable_if<!std::is_copy_assignable<U>::value, void>::type* = 0) const { }

    template< typename U = TType > b8 equalsCheck(const void* _pFirst, const void* _pSecond, typename std::enable_if<has_equals_operator<U>::value, U>::type* = 0) const { return *reinterpret_cast<const TType*>(_pFirst) == *reinterpret_cast<const TType*>(_pSecond); }
    template< typename U = TType > b8 equalsCheck(const void* _pFirst, const void* _pSecond, typename std::enable_if<!has_equals_operator<U>::value, void>::type* = 0) const { return false; }
};

Viewing all articles
Browse latest Browse all 22006

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>