In C++11 and/or C++1y:
Suppose I am given a template with a non-type parameter pack:
template<int...>
void f();
And I'm writing another template that will instantiate it:
template<int... x>
void g()
{
???
f<???>();
}
I want g to instantiate f with x in sorted order.
That is:
g<4,7,2,9,3,7>();
should call:
f<2,3,4,7,7,9>();
Can this be done? If so, what is the most efficient way (up to constant factors)?
All those answers are so depressingly C++11... lots and lots of template meta-programming spew.
Here is C++14 solution using plain sort constexpr function.
(compile and run with clang + libc++ trunk with std=c++1y)
#include <utility>
#include <iostream>
template<int... x>
void f()
{
constexpr int x_array[] = {x...};
for(int i = 0; i < sizeof...(x); i++)
std::cout << x_array[i] << " ";
std::cout << std::endl;
}
template <typename T, int N>
struct ConstArray
{
T data[N];
constexpr T& operator[](int i){return data[i];}
constexpr const T& operator[](int i) const {return data[i];}
};
template<int... x>
constexpr auto bubble_sort_best_sort()
{
constexpr int N = sizeof...(x);
ConstArray<int, N> a = {x...};
for (int i = 0; i < N - 1; i++)
{
for (int j = 0; j < N - i - 1; j++)
{
if (a.data[j] > a.data[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1]= temp;
}
}
}
return a;
}
template<int... x, int...i>
void g_imp(std::integer_sequence<int, x...>,
std::integer_sequence<int, i...> )
{
constexpr auto array_sorted = bubble_sort_best_sort<x...>();
f<array_sorted[i]...>();
}
template<int... x>
void g()
{
auto seq = std::integer_sequence<int, x...>();
auto idx = std::make_integer_sequence<int, sizeof...(x)>();
g_imp(seq, idx);
}
int main()
{
g<4, 7, 2, 9, 3, 7>();
return 0;
}
It's a bit strange that we are forced to define a custom ConstantArray instead of using std::array.
std::array could be fine here if only its "T& operator[]" member would have been constexpr. I checked in the latest draft and it's still not the case, but I don't understand why.
Here is a working solution (my first attempt). Your code would look like this:
template<int...N>
void f()
{
//this line is just to generate compilation error so that
//you can see the sorted ints in the error message
list<N...> generate_error = 0;
}
template<int...N>
void invoke_f_with(list<N...>)
{
f<N...>();
}
template<int...N>
void g()
{
invoke_f_with(typename sort<list<N...>>::type{});
}
As I intended, the generated error message contains this:
main.cpp: In instantiation of ‘void f() [with int ...N = {2, 3, 4, 7, 7, 9}]’:
That shows the integer template arguments are sorted.
The above solution makes use of sort<>
and list<>
class templates which are implemented as:
#include <type_traits>
template<int ...N>
struct list { using type = list<N...>; };
template<int N, typename IntList>
struct prepend;
template<int N, int ... ints>
struct prepend<N, list<ints...>> : list<N, ints...> {};
namespace detail
{
template<int A, int B>
struct min : std::integral_constant<int, (A < B ? A : B)> {};
template<int A, int B>
struct max : std::integral_constant<int, (A > B ? A : B)> {};
template<int i, int ...ints>
struct insert_impl : list<i> {};
template<int i, int A, int ...ints>
struct insert_impl<i, A, ints...> : prepend<min<i,A>{}, typename insert_impl<max<i,A>{}, ints...>::type> {};
template<int i, typename IntList>
struct insert;
template<int i, int ...ints>
struct insert<i, list<ints...>> : insert_impl<i, ints...> {};
}
template<typename IntList>
struct sort : list<> {};
template<int A, int ...N>
struct sort<list<A,N...>> : detail::insert<A, typename sort<list<N...>>::type> {};
Online Demo.
Hope that helps. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With