2

Introduction

What I want to do is: Regardless of the order of template arguments, I want the objects which contain the same templates, to also have the same return value of Family::identifier(). What I tried to do could be considered an answer to Is there a way to consistently sort type template parameters?.

I've taken some inspiration from the answer with the link to https://godbolt.org/z/KPc9Kd, but the main difference is that I do is Quick Sorting, while what the answer suggests using Merge Sort, so not that similar. I wanted to not just copy paste the code from there, so I decided to make my own implementation so that I learn something.

My possible solution:

My code almost, works, but there are some hiccups

Family.h

#pragma once

#include <type_traits>

struct TypeIndex 
{
    inline static size_t counter = 0;
    
    template<typename T>
    static size_t get() 
    { 
        static size_t value = counter++;
        return value;
    }
};

template <typename... Ts>
class Family 
{
    template<typename T>
    struct TypeID
    {
        static char storage;
        static constexpr void* const value = &storage;
    };

    template <typename T, typename U>
    static constexpr bool isSmaller()
    {
        return TypeID<T>::value < TypeID<U>::value;
    }

    template <typename... Ts>
    struct List { };

    template <typename... Ts>
    struct Concat;

    template <typename... Ts, typename... Us>
    struct Concat<List<Ts...>, List<Us...>> {
        using type = List<Ts..., Us...>;
    };

    template <typename... Ts>
    struct Append;

    template <typename... Ts, typename T>
    struct Append<List<Ts...>, T> {
        using type = List<Ts..., T>;
    };

    template <typename Pivot, typename List, template <typename, typename> class Condition>
    struct Partition;

    template <typename Pivot, template <typename, typename> class Condition>
    struct Partition<Pivot, List<>, Condition> {
        using type = List<>;
    };

    template <typename Pivot, typename Head, typename... Tail, template <typename, typename> class Condition>
    struct Partition<Pivot, List<Head, Tail...>, Condition> {
        using type = typename std::conditional_t<
            Condition<Pivot, Head>::value,
            typename Append<typename Partition<Pivot, List<Tail...>, Condition>::type, Head>::type,
            typename Partition<Pivot, List<Tail...>, Condition>::type
        >;
    };

    template <typename Pivot, typename T>
    struct Less {
        static constexpr bool value = isSmaller<T, Pivot>();
    };

    template <typename Pivot, typename T>
    struct Greater {
        static constexpr bool value = !isSmaller<T, Pivot>();
    };

    template <typename List>
    struct QuickSort;

    template <typename Pivot, typename... Tail>
    struct QuickSort<List<Pivot, Tail...>> {

        using LowerPartition = typename Partition<Pivot, List<Tail...>, Less>::type;
        using UpperPartition = typename Partition<Pivot, List<Tail...>, Greater>::type;

        using SortedLowerPartition = typename QuickSort<LowerPartition>::type;
        using SortedUpperPartition = typename QuickSort<UpperPartition>::type;

        using type = typename Concat<
            typename Append<SortedLowerPartition, Pivot>::type,
            SortedUpperPartition
        >::type;
    };

    template <>
    struct QuickSort<List<>> {
        using type = List;
    };

public:
    static size_t identifier()
    {
        return TypeIndex::get<typename QuickSort<List<Ts...>>::type>();
    }
};

An example of main.cpp:

...

class c1 {};
class c2 {};
class c3 {};
class c4 {};

int main()
{
    std::cout << Family<c1, c2, c3, c4>::identifier() << '\n';
    std::cout << Family<c1, c3, c2, c4>::identifier() << '\n';
    std::cout << Family<c2, c1, c3>::identifier() << '\n';
    std::cout << Family<c4, c3, c1>::identifier() << '\n';
    std::cout << Family<c3        >::identifier() << '\n';
    std::cout << Family<c3, c2, c1>::identifier() << '\n';
    
    ...
}

The errors

When I try to compile this code, it complains about the QuickSort::type

using type = typename Concat<
            typename Append<SortedLowerPartition, Pivot>::type,
            SortedUpperPartition
        >::type;

Error C2027 use of undefined type 'Family<c3,c2,c1>::Append<int,Pivot>'

Error C2027 use of undefined type 'Family<c3,c2,c1>::Append<int,Pivot>'

Error C2027 use of undefined type 'Family<c3,c2,c1>::Append<int,Pivot>'

Error C2146 syntax error: missing '>' before identifier 'type'

Error C2146 syntax error: missing ';' before identifier 'type'

Error C2039 'type': is not a member of '`global namespace''

Error C3203 'List': unspecialized class template can't be used as a template argument for template parameter 'Ts', expected a real type

Error C2602 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List>::type' is not a member of a base class of 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List>'

Error C2602 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List<c3,c2,c1>>::type' is not a member of a base class of 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List<c3,c2,c1>>'

Error C2602 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List<c1,T>>::type' is not a member of a base class of 'Family<c3,c2,c1>::QuickSort<Family<c3,c2,c1>::List<c1,T>>'

What I suspect is happening

I've tested the Partition structure and it works fine -everything else seems to be fine too. One possible indication for the underlying problem could be that when I replace the SortedLowerPartition, and the SortedUpperPartition with LowerPartition, and also UpperPartition. Everything compiles fine. ( Note that this would also mean that there is no more recursion in the Quick Sort ). This may mean that SortedLowerPartition and LowerPartition are not of type List<>, as I expected them to be. This would be weird because Concat<>::type should be a List<>, and therefore also the SortedLowerPartition<> should be List's. However, this seems not to be the case here.

Any help would be very much appreciated, I'm going insane here

bibanac
  • 135
  • 7
  • It seems that using Microsoft's compiler will overlook many of the issues in this code that [other compilers will report](https://godbolt.org/z/vnnvqTMYc). – Drew Dormann Jul 07 '23 at 16:00
  • `TypeID::value < TypeID::value;` (your `isSmaller`) is just not going to be a constant expression (you can't compare unrelated pointers). Also `Family::TypeId::value` is different from `Family::TypeId::value` because you define `TypeId` inside `Family` – Artyer Jul 07 '23 at 16:06
  • The isSmaller comparator is inspired from: https://stackoverflow.com/questions/7562096/compile-time-constant-id (the first answer) @Artyer. I really didnt like the solution provided here: https://godbolt.org/z/KPc9Kd. On my MSVC compiler, everything else seemed fine – bibanac Jul 07 '23 at 16:15
  • @bibanac You can define TypeID as you did. You can not compare unrelated pointers with `<`. You can compare unrelated pointers with `std::less`. Standard isn't clear on whether you can do that in compile-time: https://stackoverflow.com/questions/62492020/is-stdless-supposed-to-allow-comparison-of-unrelated-pointers-at-compile-time. – maxplus Jul 07 '23 at 16:30
  • https://godbolt.org/z/GW7MbWMod This works now, I hate MSVC. MSVC mentioned nothing about this. – bibanac Jul 07 '23 at 16:31
  • @bibanac that code technically is ill-formed, no diagnostics required on unsupported compilers because there is no `T` for which `type_name` is valid. But for the three supported ones seems OK – maxplus Jul 07 '23 at 16:38
  • I suggest to replace `std::conditional_t::type, typename Func2::type>` by `typename std::conditional_t, Func2>::type` to avoid some instantiations. – Jarod42 Jul 07 '23 at 16:40
  • Are there any "correct" methods to implement the isSmaller() function? – bibanac Jul 07 '23 at 16:50
  • @bibanac I think what you implemented is actually the best that can be done currently and there's no standard solution, but I wouldn't call myself an expert. Similar attempts were made, it would be a logical feature to implement in [Boost hana](https://www.boost.org/doc/libs/1_63_0/libs/hana/doc/html/structboost_1_1hana_1_1type.html), yet they support only equality comparisons. – maxplus Jul 07 '23 at 16:58
  • "I am new to programming" — nice one, btw ;) – maxplus Jul 07 '23 at 16:58
  • 1
    Technically I've been programming for 6 years, but im still in highschool, and I also didnt want to get flamed on, because my code may be actual garbage, that's why I usualy start my questions with "I'm new to programming" @maxplus – bibanac Jul 07 '23 at 17:03
  • Im currently writing a game engine and this is supposed to be used for the indexing for the ECS, for the indexing of archetypes. This is so very much overengineering and i know it, i don't even know if ill need this code, but it was so much fun writing it – bibanac Jul 07 '23 at 17:06

1 Answers1

4

What didn't work?

Apperantly my MSVC compiler glossed over the actual problem, and only showed me some "fake problems".

The actual problem

What I had to do to make the program compile correctly was to modify the isSmaller() function. Apparently I can't compare memory adresses at compile time, so I had to "inspire myself" from this beautiful answer: Is there a way to consistently sort type template parameters?

template <typename T>
constexpr auto type_name() noexcept {
  std::string_view name = "Error: unsupported compiler", prefix, suffix;
#ifdef __clang__
  name = __PRETTY_FUNCTION__;
  prefix = "auto type_name() [T = ";
  suffix = "]";
#elif defined(__GNUC__)
  name = __PRETTY_FUNCTION__;
  prefix = "constexpr auto type_name() [with T = ";
  suffix = "]";
#elif defined(_MSC_VER)
  name = __FUNCSIG__;
  prefix = "auto __cdecl type_name<";
  suffix = ">(void) noexcept";
#else
  static_assert(false, "Unsupported compiler!");
#endif
  name.remove_prefix(prefix.size());
  name.remove_suffix(suffix.size());
  return name;
}

How does this code work?

This templated function snatches the type, then uses a compiler specific macro to actually get a std::string_view specific for the type, which can then be used for the isSmaller() function

You can find a working version here: https://godbolt.org/z/nEvo44xd3

bibanac
  • 135
  • 7