1

Does C++ provide an ordering of the set of all types as a constant expression? It doesn't matter which particular order, any one will do. This could be in form of a constexpr comparison function:

template <typename T1, typename T2>
constexpr bool TypeLesser ();

My use for this is for a compile time self-balancing binary search tree of types, as a replacement of (cons/nil) type lists, to speed up the compilation. For example, checking whether a type is contained in such a tree may be faster than checking if it is contained in a type list.

I will also accept compiler-specific intrinsics if standard C++ does not provide such a feature.

Note that if the only way to get an ordering is to define it manually by adding boilerplate all over the code base (which includes a lot of templates and anonymous structs), I will rather stay with type lists.

Ambroz Bizjak
  • 7,809
  • 1
  • 38
  • 49
  • Compare [`typeid`](http://en.cppreference.com/w/cpp/language/typeid)'s maybe? – πάντα ῥεῖ Jul 05 '14 at 19:42
  • @πάνταῥεῖ There doesn't seem to anything useful in std::type_info with regard to my problem. Note that I'm looking for a constexpr ordering. – Ambroz Bizjak Jul 05 '14 at 19:49
  • I suppose a strict weak ordering is not sufficient for a BST. I was thinking of `sizeof(T)`... – NicholasM Jul 05 '14 at 19:58
  • There's [`type_index`](http://en.cppreference.com/w/cpp/types/type_index), but it's not `constexpr` – Praetorian Jul 05 '14 at 20:04
  • @Praetorian: Since the totality of all types depends on seeing all translation units, it seems unlikely that there'll be a constexpr ordering. At least not without special compiler and linker support. – Kerrek SB Jul 05 '14 at 21:31
  • @KerrekSB I'm only looking for an ordering within a translation unit. – Ambroz Bizjak Jul 06 '14 at 06:10
  • Note that for your example, you don't need recursion to know if `T` is part of a collection of types: [Demo](http://coliru.stacked-crooked.com/a/c9f84a9746783fd0). – Jarod42 Sep 29 '16 at 12:22

2 Answers2

0

The standard’s only ordering is via type_info (provided by typeid expression), which you can use more easily via type_index – the latter provides ordinary comparison functionality so that it can be used in collections.

I guess its ancestry is the class Andrei Alexandrescu had in “Modern C++ Design”.

It's not compile time.


To reduce compilation time you can define traits classes for the types in question, assigning each type some ordinal value. A 128 bit UUID would do nicely as a type id, to avoid the practical issue of guaranteeing unique id's. This of course assumes that you or the client code controls the set of possible types.

The idea of having to "register" relevant types has been used before, in early Boost machinery for determining function result types.


I must anyway recommend seriously measuring compilation performance. The balancing operations that are fast at run time, involving only adjustment of a few pointers, may be slow at compile time, involving creating a huge descriptor of a whole new type. So even though checking for type set membership may be faster, building the type set may be seriously much slower, e.g. O(n2).

Disclaimer: I haven't tried that.

But anyway, I remember again that Andrei Alexandrescu discussed something of the sort in the already mentioned “Modern C++ Design”, or if you don't have access to that book, look in the Loki library (which is a library of things from that book).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

You have two main problems: 1) You have no specific comparison criteria (Hence the question, isn't?), and 2) You don't have any standard way to sort at compile-time.

For the first use std::type_info as others suggested (Its currently used on maps via the std::type_index wrapper) or define your own metafunction to specify the ordering criteria for different types. For the second, you could try to write your own template-metaprogramming based quicksort algorithm. Thats what I did for my personal metaprogramming library and works perfectly.

About the assumption "A self-balancing search tree should perform better than classic typelists" I really encourage you to do some profillings (Try templight) before saying that. Compile-time performance has nothing to do with classic runtime performance, depends heavily in the exact implementation of the template instantation system the compiler has.
For example, based on my own experience I'm pretty sure that my simple "O(n)" linear search could perform better than your self balanced tree. Why? Memoization. Compile-time performance is not only instantation depth. In fact memoization has a crucial role on this.

To give you a real example: Consider the implementation of quicksort (Pseudo meta code):

list sort( List l )
{
    Int pivot = l[l.length/2];

    Tuple(List,List) lists = reorder( l , pivot , l.length/2 );

    return concat( sort( lists.left ) , sort( lists.right ) );
}

I hope that example is self-explanatory. Note the functional way it works, there are no side effects. I will be glad if some day metaprogramming in C++ has that syntax...

Thats the recursive case of quicksort. Since we are using typelists (Variadic typelists in my case), the first metainstruction, which computes the value of the pivot has O(n) complexity. Specifically requires a template instantation depth of N/2. The seconds step (Reordering) could be done in O(n), and concatting is O(1) (Remember that are C++11 variadic typelists).

Now consider an example of execution:

[1,2,3,4,5]

The first step calls the recursive case, so the trace is:

  • Int pivot = l[l.length/2]; traverses the list until 3. That means the instantations needed to perform the traversings [1], [1,2], [1,2,3] are memoized.

  • During the reordering, more subtraversings (And combinations of subtraversing generated by element "swapping") are generated.

  • Recursive "calls" and concat.

Since such linear traversings performed to go to the middle of the list are memoized, they are instantiated only once along the whole sort execution. When I first encountered this using templight I was completely dumbfounded. The fact, looking at the instantations graph, is that only the first large traverses are instantiated, the little ones are just part of the large and since the large where memoized, the little are not instanced again.

So wow, the compiler is able of memoizing at least the half of that so slow linear traversings, right? But, what is the cost of such enormous memoization efforts?

What I'm trying to say with this answer is: When doing template meta-programming, forget everything about runtime performance, optimizations, costs, etc, and don't do assumptions. Measure. You are entering in a completely different league. I'm not completely sure what implementation (Your selft balancing trees vs simple linear traversing) is faster, because that depends on the compiler. My example was only to show how actually a compiler could break down completely your assumptions.

Side note: The first time I did that profilings I showed them to an algorithms teacher of my university, and he's still trying to figure out whats happening. In fact, he asked a question here about how to measure the complexity and performance of this monster: Best practices for measuring the run-time complexity of a piece of code

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • Thanks for the info, but I wasn't actually asking for a detailed explanation on compile time versus runtime performance. Of course I was planning to profile. But I can't do that without a comparison function. And you haven't provided a way to do that (type_info is not compile time). Defining custom comparison is out of the question due to the extreme number of different types concerned (incl. template classes and anonymous classes) which I suspect would need boilerplate all around the code. – Ambroz Bizjak Jul 06 '14 at 06:06