variadic template: SFINAE on last argument - c++

I have an array (of any rank), and I would like to have an index operator that:
Allows for missing indices, such that the following is equivalent
a(1, 0, 0, 0);
a(1, my::missing);
This in itself if straightforward (see example implementation below): one just recursively adds arg * strides[dim] until my::missing it hit.
Allows automatic prepending of zeros, such that the following is equivalent
a(0, 0, 0, 1);
a(1);
Also this is not hard (see example implementation below): one recursively adds arg * strides[dim + offset].
What I cannot get my head around is: How to combine the two? The implementation of 2. makes me start of the wrong foot for 1. (I'm limited to <= C++14)
Example implementation of "my::missing" without auto pre-pending zeros
enum class my { missing };
template <size_t dim, class S>
inline size_t index_impl(const S&) noexcept
{
return 0;
}
template <size_t dim, class S, class... Args>
inline size_t index_impl(const S& strides, enum my arg, Args... args) noexcept
{
return 0;
}
template <size_t dim, class S, class Arg, class... Args>
inline size_t index_impl(const S& strides, Arg arg, Args... args) noexcept
{
return arg * strides[dim] + index_impl<dim + 1>(strides, args...);
}
template <class S, class Arg, class... Args>
inline size_t index(const S& strides, Arg arg, Args... args)
{
return index_impl<0>(strides, arg, args...);
}
int main()
{
std::vector<size_t> strides = {8, 4, 2 ,1};
std::cout << index(strides, 1, 2, 0, 0) << std::endl;
std::cout << index(strides, 1, 2, my::missing) << std::endl;
}
Example implementation of prepending zeros without "my::missing"
template <size_t dim, class S>
inline size_t index_impl(const S&) noexcept
{
return 0;
}
template <size_t dim, class S, class Arg, class... Args>
inline size_t index_impl(const S& strides, Arg arg, Args... args) noexcept
{
return arg * strides[dim] + index_impl<dim + 1>(strides, args...);
}
template <class S, class Arg, class... Args>
inline size_t index(const S& strides, Arg arg, Args... args)
{
constexpr size_t nargs = sizeof...(Args) + 1;
if (nargs == strides.size())
{
return index_impl<0>(strides, arg, args...);
}
else if (nargs < strides.size())
{
return index_impl<0>(strides.cend() - nargs, arg, args...);
}
return index_impl<0>(strides, arg, args...);
}
int main()
{
std::vector<size_t> strides = {8, 4, 2 ,1};
std::cout << index(strides, 1, 2) << std::endl;
std::cout << index(strides, 0, 0, 1, 2) << std::endl;
}

In earlier version of this answer I didn't provided full implementation since something was not adding up for me.
If this index should calculate index for flattened multidimensional array then your example implementation is invalid. Problem is hidden since you are comparing two results for index with all indexes provided and shorten version where zero padding is assumed.
Sadly I flowed this pattern in first versions of test in Catch2.
Here is proper test for index of flattened multidimensional array, where last index matches flattened index:
TEST_CASE("index")
{
std::vector<size_t> strides = { 4, 6, 3, 5 };
SECTION("Padding with leading zeros")
{
constexpr auto i0 = 4;
constexpr auto i1 = 2;
constexpr size_t expected = i0 + i1 * 5;
CHECK(index(strides, 0, 0, i1, i0) == expected);
CHECK(index(strides, 0, 0, i1, i0 - 1) == expected - 1); // last index indexes by one
CHECK(index(strides, i1, i0) == expected);
CHECK(index(strides, i1, i0 - 1) == expected - 1);
}
SECTION("Use my::missing to use padding with tailing zeros")
{
constexpr auto i2 = 4;
constexpr auto i3 = 2;
constexpr size_t expected = (i3 * 6 + i2) * 5 * 3;
CHECK(index(strides, i3, i2, 0, 0) == expected);
CHECK(index(strides, i3, i2, my::missing) == expected);
}
}
Now starting from your code and passing those test I've got this implementation:
template <typename T, typename... Ts>
struct last_type_helper {
using type = typename last_type_helper<Ts...>::type;
};
template <typename T>
struct last_type_helper<T> {
using type = T;
};
template <typename... Ts>
using last_type = typename last_type_helper<Ts...>::type;
enum class my { missing };
template <typename... Ts>
constexpr bool LastTypeIsMy = std::is_same<my, last_type<Ts...>>::value;
template <class StrideIter>
size_t index_impl(size_t base, StrideIter)
{
return base;
}
template <class StrideIter>
size_t index_impl(size_t base, StrideIter, my)
{
return base;
}
template <class StrideIter, typename Tn, typename... Ts>
size_t index_impl(size_t base, StrideIter it, Tn xn, Ts... x)
{
return index_impl(base * *it + xn, it + 1, x...);
}
template <class S, class... Args>
size_t index(const S& strides, Args... args)
{
const size_t offset = strides.size() - sizeof...(Args);
const size_t advenceBy = LastTypeIsMy<Args...> ? 0 : offset;
const size_t lastStrides = LastTypeIsMy<Args...> ? offset + 1 : 0;
const size_t tailFactor = std::accumulate(std::end(strides) - lastStrides, std::end(strides),
size_t { 1 }, std::multiplies<> {});
return index_impl(0, std::begin(strides) + advenceBy, args...) * tailFactor;
}
Here is live demo (passing tests).

C++14 distinctively lacks fold expressions, which usually makes parameter packs much harder to work with. Thankfully, we can use array initializations to fake fold expressions, which is allowed in constexpr functions, so we can get away with no recursion and better code readability.
template<typename... Idxs>
constexpr int indexing_type()
{
size_t integrals = 0;
bool unused[] = {(integrals += std::is_integral<Idxs>::value, false)...};
(void)unused;
bool mys[] = {std::is_same<my, Idxs>::value...};
bool last = mys[sizeof(mys) - 1];
if(integrals == sizeof...(Idxs))
return 1;
if(integrals == sizeof...(Idxs) - 1 && last)
return 2;
return 0;
}
template<typename S, size_t... Is, typename... Idxs>
inline auto mul_reduce(const S& strides, size_t off, std::index_sequence<Is...>, Idxs... idxs)
{
size_t sum = 0;
bool unused[] = {(sum += strides[off + Is] * size_t(idxs), false)...};
(void)unused;
return sum;
}
template<typename S, typename... Idxs>
inline auto index(const S& strides, Idxs... idxs)
-> std::enable_if_t<indexing_type<Idxs...>() == 1, size_t>
{
auto off = strides.size() - sizeof...(Idxs);
return mul_reduce(strides, off, std::make_index_sequence<sizeof...(Idxs)>{}, idxs...);
}
template<typename S, typename... Idxs>
inline auto index(const S& strides, Idxs... idxs)
-> std::enable_if_t<indexing_type<Idxs...>() == 2, size_t>
{
return mul_reduce(strides, 0, std::make_index_sequence<sizeof...(Idxs)>{}, idxs...);
}
indexing_type tells you which function to dispatch to, and also disables index from overload resolution in case the parameter types are wrong.
In mul_reduce, we abuse the fact that size_t(missing) == 0 to avoid needing to remove it from the end.

Related

Cartesian product for multiple sets at compile time

I am struggling with an implementation of the Cartesian product for
multiple indices with a given range 0,...,n-1.
The basic idea is to have a function:
cartesian_product<std::size_t range, std::size_t sets>()
with an output array that contains tuples that hold the different products
[(0,..,0), (0,...,1), (0,...,n-1),...., (n-1, ..., n-1)]
An simple example would be the following:
auto result = cartesian_product<3, 2>();
with the output type std::array<std::tuple<int, int>, (3^2)>:
[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
My main problem is that my version of the Cartesian product is slow and creates a stack overflow if you choose to have more than 5 sets. I believe that my code has too many recursions and temporary variables.
My implementation (C++17) can be found here: cartesian_product
#include <stdio.h>
#include <iostream>
#include <tuple>
template<typename T, std::size_t ...is>
constexpr auto flatten_tuple_i(T tuple, std::index_sequence<is...>) {
return std::tuple_cat(std::get<is>(tuple)...);
}
template<typename T>
constexpr auto flatten_tuple(T tuple) {
return flatten_tuple_i(tuple, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<std::size_t depth, typename T>
constexpr auto recursive_flatten_tuple(T tuple){
if constexpr(depth <= 1){
return tuple;
}else{
return recursive_flatten_tuple<depth-1>(flatten_tuple(tuple));
}
}
template<std::size_t depth, typename T, std::size_t ...is>
constexpr auto wdh(T&& tuple, std::index_sequence<is...>){
if constexpr (depth == 0) {
return tuple;
}else{
//return (wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)),std::make_index_sequence<sizeof...(is)>{})...);
return std::make_tuple(wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)), std::make_index_sequence<sizeof...(is)>{})...);
}
}
template<std::size_t sets, typename T, std::size_t ...is>
constexpr auto to_array(T tuple, std::index_sequence<is...>){
if constexpr (sets == 0){
auto t = (std::make_tuple(std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::make_tuple(std::get<is>(tuple))...};
//decltype(arr)::foo = 1;
return arr;
}else{
auto t = ((std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::get<is>(tuple)...};
return arr;
}
}
template<std::size_t sets, std::size_t ...is>
constexpr auto ct_i(std::index_sequence<is...>){
if constexpr (sets == 0){
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto arr = to_array<sets>(u, std::make_index_sequence<std::tuple_size<decltype(u)>::value>{});
return arr;
}else {
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto r = recursive_flatten_tuple<sets>(u);
auto d = to_array<sets>(r, std::make_index_sequence<std::tuple_size<decltype(r)>::value>{});
return d;
}
}
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product(){
static_assert( (range > 0), "lowest input must be cartesian<1,1>" );
static_assert( (sets > 0), "lowest input must be cartesian<1,1>" );
return ct_i<sets-1>(std::make_index_sequence<range>{});
}
int main()
{
constexpr auto crt = cartesian_product<3, 2>();
for(auto&& ele : crt){
std::cout << std::get<0>(ele) << " " << std::get<1>(ele) << std::endl;
}
return 0;
}
Since I was also working on a solution I thought I post it aswell (although very similar to Artyer's answer). Same premise, we replace the tuple with an array and just iterate over the elements, incrementing them one by one.
Note that the power function is broken, so if you need power values <0 or non-integer types you have to fix it.
template <typename It, typename T>
constexpr void setAll(It begin, It end, T value)
{
for (; begin != end; ++begin)
*begin = value;
}
template <typename T, std::size_t I>
constexpr void increment(std::array<T, I>& counter, T max)
{
for (auto idx = I; idx > 0;)
{
--idx;
if (++counter[idx] >= max)
{
setAll(counter.begin() + idx, counter.end(), 0); // because std::fill is not constexpr yet
}
else
{
break;
}
}
}
// std::pow is not constexpr
constexpr auto power = [](auto base, auto power)
{
auto result = base;
while (--power)
result *= base;
return result;
};
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product()
{
std::array<std::array<int, sets>, power(range, sets)> products{};
std::array<int, sets> currentSet{};
for (auto& product : products)
{
product = currentSet;
increment(currentSet, static_cast<int>(range));
}
return products;
}
int main()
{
constexpr auto crt = cartesian_product<5, 3>();
for (auto&& ele : crt)
{
for (auto val : ele)
std::cout << val << " ";
std::cout << "\n";
}
return 0;
}
Example
With Boost.Mp11, this is... alright, it's not a one-liner, but it's still not so bad:
template <typename... Lists>
using list_product = mp_product<mp_list, Lists...>;
template <typename... Ts>
constexpr auto list_to_tuple(mp_list<Ts...>) {
return std::make_tuple(int(Ts::value)...);
}
template <typename... Ls>
constexpr auto list_to_array(mp_list<Ls...>) {
return std::array{list_to_tuple(Ls{})...};
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using L = mp_repeat_c<mp_list<mp_iota_c<R>>, N>;
return list_to_array(mp_apply<list_product, L>{});
}
With C++20, you can declare the two helper function templates as lambdas inside of cartesian_product, which makes this read nicer (top to bottom instead of bottom to top).
Explanation of what's going on, based on the OP example of cartesian_product<3, 2>:
mp_iota_c<R> gives us the list [0, 1, 2] (but as integral constant types)
mp_repeat_c<mp_list<mp_iota_c<R>>, N> gives us [[0, 1, 2], [0, 1, 2]]. We just repeat the list, but we want a list of lists (hence the extra mp_list in the middle).
mp_apply<list_product, L> does mp_product, which is a cartesian product of all the lists you pass in... sticking the result in an mp_list. This gives you [[0, 0], [0, 1], [0, 2], ..., [2, 2]], but as an mp_list of mp_list of integral constants.
At this point the hard part is over, we just have to convert the result back to an array of tuples. list_to_tuple takes an mp_list of integral constants and turns that into a tuple<int...> with the right values. And list_to_array takes an mp_list of mp_lists of integral constants and turns that into an std::array of tuples.
A slightly different approach using just the single helper function:
template <template <typename...> class L,
typename... Ts, typename F>
constexpr auto unpack(L<Ts...>, F f) {
return f(Ts{}...);
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using P = mp_apply_q<
mp_bind_front<mp_product_q, mp_quote<mp_list>>,
mp_repeat_c<mp_list<mp_iota_c<R>>, N>>;
return unpack(P{},
[](auto... lists){
return std::array{
unpack(lists, [](auto... values){
return std::make_tuple(int(values)...);
})...
};
});
}
This approach is harder to read though, but it's the same algorithm.
You can do this without recursion easily. Notice that each tuple is the digits of numbers from 0 to range ** sets in base range, so you could increment a counter (Or apply to a std::index_sequence) and calculate each value one after the other.
Here's an implementation (That returns a std::array of std::arrays, which works mostly the same as std::tuples as you can get<N>, tuple_size and tuple_element<N> on a std::array, though if you really wanted you can convert them to std::tuples):
#include <cstddef>
#include <array>
namespace detail {
constexpr std::size_t ipow(std::size_t base, std::size_t exponent) noexcept {
std::size_t p = 1;
while (exponent) {
if (exponent % 2 != 0) {
p *= base;
}
exponent /= 2;
base *= base;
}
return p;
}
}
template<std::size_t range, std::size_t sets>
constexpr std::array<std::array<std::size_t, sets>, detail::ipow(range, sets)>
cartesian_product() noexcept {
constexpr std::size_t size = detail::ipow(range, sets);
std::array<std::array<std::size_t, sets>, size> result{};
for (std::size_t i = 0; i < size; ++i) {
std::size_t place = size;
for (std::size_t j = 0; j < sets; ++j) {
place /= range;
result[i][j] = (i / place) % range;
}
}
return result;
}
Here's a test link: https://www.onlinegdb.com/By_X9wbrI
Note that (empty_set)^0 is defined as a set containing an empty set here, but that can be changed by making ipow(0, 0) == 0 instead of 1
I was trying it out just for fun and I ended with pretty much the same idea as #Timo, just with a different format/style.
#include <iostream>
#include <array>
using namespace std;
template<size_t range, size_t sets>
constexpr auto cartesian_product() {
// how many elements = range^sets
constexpr auto size = []() {
size_t x = range;
size_t n = sets;
while(--n != 0) x *= range;
return x;
}();
auto products = array<array<size_t, sets>, size>();
auto counter = array<size_t, sets>{}; // array of zeroes
for (auto &product : products) {
product = counter;
// counter increment and wrapping/carry over
counter.back()++;
for (size_t i = counter.size()-1; i != 0; i--) {
if (counter[i] == range) {
counter[i] = 0;
counter[i-1]++;
}
else break;
}
}
return products;
}
int main() {
auto prods = cartesian_product<3, 6>();
}
I basically have a counter array which I increment manually, like so:
// given cartesian_product<3, 4>
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 1, 0] // carry over
...
...
[2, 2, 2, 2]
Pretty much just how you would do it by hand.
Example
If you want it in compile-time, you should only employ compile-time evaluations over compile-time data structures. As #Barry pointed above, using Boost.Mp11 greatly facilitates it. Of course you can do reimplement the relevant fundamental functions in plain C++17 on your own:
#include <iostream>
template<class T> struct Box {
using type = T;
};
template<class... Types> struct List {};
template<class Car, class Cdr> struct Cons;
template<class Car, class Cdr> using ConsT = typename Cons<Car, Cdr>::type;
template<class Car, class... Cdr> struct Cons<Car, List<Cdr...>>: Box<List<Car, Cdr...>> {};
using Nil = List<>;
template<std::size_t i, class L> struct Nth;
template<std::size_t i, class L> using NthT = typename Nth<i, L>::type;
template<std::size_t i, class... Ts> struct Nth<i, List<Ts...>>: std::tuple_element<i, std::tuple<Ts...>> {};
template<class L> struct Head;
template<class L> using HeadT = typename Head<L>::type;
template<class Car, class... Cdr> struct Head<List<Car, Cdr...>>: Box<Car> {};
template<class L> struct Tail;
template<class L> using TailT = typename Tail<L>::type;
template<class Car, class... Cdr> struct Tail<List<Car, Cdr...>>: Box<List<Cdr...>> {};
template<class... Lists> struct Concat;
template<class... Lists> using ConcatT = typename Concat<Lists...>::type;
template<class T, class... Rest> struct Concat<T, Rest...>: Cons<T, ConcatT<Rest...>> {};
template<class Head, class... Tail, class... Rest> struct Concat<List<Head, Tail...>, Rest...>: Cons<Head, ConcatT<List<Tail...>, Rest...>> {};
template<class... Rest> struct Concat<Nil, Rest...>: Concat<Rest...> {};
template<> struct Concat<>: Box<Nil> {};
template<class T, class Subspace> struct Prepend;
template<class T, class Subspace> using PrependT = typename Prepend<T, Subspace>::type;
template<class T, class... Points> struct Prepend<T, List<Points...>>: Box<List<ConsT<T, Points>...>> {};
template<class T> struct Prepend<T, Nil>: Box<List<List<T>>> {};
template<class Range, class Subspace> struct Product;
template<class Range, class Subspace> using ProductT = typename Product<Range, Subspace>::type;
template<class Range, class Subspace> struct Product: Concat<PrependT<HeadT<Range>, Subspace>, ProductT<TailT<Range>, Subspace>> {};
template<class Subspace> struct Product<Nil, Subspace>: Box<Nil> {};
template<std::size_t i> using IntValue = std::integral_constant<std::size_t, i>;
template<class Seq> struct IntegerSequence;
template<class Seq> using IntegerSequenceT = typename IntegerSequence<Seq>::type;
template<std::size_t... is> struct IntegerSequence<std::index_sequence<is...>>: Box<List<IntValue<is>...>> {};
template<std::size_t n> using Range = IntegerSequenceT<std::make_index_sequence<n>>;
template<std::size_t dimensions, std::size_t range> struct CartesianCube;
template<std::size_t dimensions, std::size_t range> using CartesianCubeT = typename CartesianCube<dimensions, range>::type;
template<std::size_t dimensions, std::size_t range> struct CartesianCube: Product<Range<range>, CartesianCubeT<dimensions - 1, range>> {};
template<std::size_t range> struct CartesianCube<0, range>: Box<Nil> {};
template<std::size_t i> std::ostream &operator<<(std::ostream &s, IntValue<i>) {
return s << '<' << i << '>';
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>);
namespace detail_ {
template<class L, std::size_t... is> std::ostream &printList(std::ostream &s, L, std::index_sequence<is...>) {
return ((s << (is == 0? "" : ", ") << NthT<is, L>{}), ...), s;
}
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>) {
return detail_::printList(s << "List{", List<Ts...>{}, std::index_sequence_for<Ts...>{}) << '}';
}
int main() {
std::cout << CartesianCubeT<2, 3>{} << '\n';
}
Note that CartesianCubeT here is actually a List of Lists of integral_constants. Once you have those, converting them into run-time values is trivial. Note that cartesian_product does not even have to be a function, since the whole data set is evaluated at compile-time it can be a templated value.

Constexpr determinant (2 dimensional std::array)

I need to write a constexpr function that computes a determinant at compile time. The most obvious solution is to use Laplace expansion. C++14 is supported.
#include <array>
#include <utility>
constexpr int get_cofactor_coef(int i, int j) {
return (i + j) % 2 == 0 ? 1 : -1;
}
template <int N>
constexpr int determinant(const std::array<std::array<int, N>, N>& a) {
int det = 0;
for (size_t i = 0u; i < N; ++i) {
det += get_cofactor_coef(i, 1) * a[i][0] * determinant<N-1>(GET_SUBMATRIX_OF_A<N-1, I, J>(a);
}
return det;
}
template <>
constexpr int determinant<2>(const std::array<std::array<int, 2>, 2>& a) {
return a[0][0] * a[1][1] - a[0][1] * a[1][0];
}
template <>
constexpr int determinant<1>(const std::array<std::array<int, 1>, 1>& a) {
return a[0][0];
}
The problem is that I have absolutely no clue how to write the GET_SUBMATRIX_OF_A.
I know that I need to:
Generate a sequence (using std::integer_sequence probably);
Exclude from this sequence the i-th row;
Copy all but the first (0-th) column;
My constexpr skills are next to non-existent. Head on attempts to pass a to another function result in weird errors like error: '* & a' is not a constant expression.
All help is greatly appreciated!
The problem is that the non-const std::array<T, N>::operator[] (returning T&) is not constexpr until C++17, making it difficult to set the elements of the minor.
However, there is an escape hatch, which is that std::get<I>(std::array&) is constexpr, and it is perfectly legitimate to perform pointer arithmetic on the result, so we can rewrite
a[i] // constexpr since C++17
as
(&std::get<0>(a))[i] // constexpr in C++14!!
That is, we use std::get to obtain a constexpr reference to the first member of the array, take a pointer to it, and use the built-in [] operator on the pointer and index.
Then a two-level array member access a[i][j] becomes the horrendously ugly but still constexpr (&std::get<0>((&std::get<0>(a))[i]))[j], meaning we can write get_submatrix_of_a as an ordinary constexpr function:
template<std::size_t N>
constexpr std::array<std::array<int, N - 1>, N - 1>
get_submatrix_of_a(const std::array<std::array<int, N>, N>& a, int i, int j) {
std::array<std::array<int, N - 1>, N - 1> r{};
for (int ii = 0; ii != N - 1; ++ii)
for (int jj = 0; jj != N - 1; ++jj)
(&std::get<0>(((&std::get<0>(r))[ii])))[jj] = a[ii + (ii >= i ? 1 : 0)][jj + (jj >= j ? 1 : 0)];
return r;
}
Remember that const std::array<T, N>::operator[] is already constexpr in C++14, so we don't need to rewrite the RHS of the minor construction.
Here's an example implementation. It might be possible to do this even shorter or more elegant, but it's a starting point. Actually, I just realized your matrices are square, so it's definitely possible to drop some template parameters in the code below.
As I mentioned in my comment, for C++17 and up, it's very likely none of this is required at all.
First, let's define some boilerplate that let's us create and index sequence with one value left out (i.e. the row you want to skip):
#include <utility>
// Based on https://stackoverflow.com/a/32223343.
template <size_t Offset, class T1, class T2>
struct offset_sequence_merger;
template <size_t Offset, size_t... I1, size_t... I2>
struct offset_sequence_merger<Offset, std::index_sequence<I1...>, std::index_sequence<I2...>>
: std::index_sequence<I1..., (Offset + I2)...>
{ };
template <std::size_t Excluded, std::size_t End>
using make_excluded_index_sequence = offset_sequence_merger<Excluded + 1,
std::make_index_sequence<Excluded>,
std::make_index_sequence<End - Excluded - 1>>;
Now let's put this to use to extract sub-matrices:
#include <array>
template <class T, std::size_t N, std::size_t... Indices>
constexpr std::array<T, sizeof...(Indices)> extract_columns (
std::array<T, N> const & source, std::index_sequence<Indices...>) {
return { source.at(Indices)... };
}
template <class T, std::size_t N>
constexpr std::array<T, N - 1> drop_first_column (
std::array<T, N> const & source) {
return extract_columns(source, make_excluded_index_sequence<0, N>());
}
template <class T, std::size_t Rows, std::size_t Cols, std::size_t... RowIndices>
constexpr auto create_sub_matrix (
std::array<std::array<T, Cols>, Rows> const & source,
std::index_sequence<RowIndices...>)
-> std::array<std::array<T, Cols - 1>, sizeof...(RowIndices)> {
return { drop_first_column(source.at(RowIndices))... };
}
template <std::size_t ExcludedRow, class T, std::size_t Rows, std::size_t Cols>
constexpr auto create_sub_matrix (
std::array<std::array<T, Cols>, Rows> const & source)
-> std::array<std::array<T, Cols - 1>, Rows - 1> {
return create_sub_matrix(source,
make_excluded_index_sequence<ExcludedRow, Rows>());
}
And finally, here's some code showing that the above seems to do what it should. You can see it in action at Wandbox:
#include <iostream>
#include <string>
template <class T>
void print_seq (std::integer_sequence<T> const & /* seq */) {
std::cout << '\n';
}
template <class T, T Head, T... Tail>
void print_seq (std::integer_sequence<T, Head, Tail...> const & /* seq */) {
std::cout << Head << ' ';
print_seq(std::integer_sequence<T, Tail...>{});
}
template <class T, std::size_t N>
void print_array (std::array<T, N> const & src) {
std::string sep = "";
for (auto const & e : src) {
std::cout << sep << e;
sep = " ";
}
std::cout << '\n';
}
template <class T, std::size_t N, std::size_t M>
void print_matrix (std::array<std::array<T, N>, M> const & src) {
for (auto const & row : src) { print_array(row); }
}
int main () {
auto indexSeqA = make_excluded_index_sequence<0, 3>(); print_seq(indexSeqA);
auto indexSeqB = make_excluded_index_sequence<1, 3>(); print_seq(indexSeqB);
auto indexSeqC = make_excluded_index_sequence<2, 3>(); print_seq(indexSeqC);
std::cout << '\n';
std::array<int, 3> arr = { 1, 7, 9 };
print_array(arr); std::cout << '\n';
std::array<std::array<int, 3>, 3> matrix = {{
{ 0, 1, 2 }
, { 3, 4, 5 }
, { 6, 7, 8 }
}};
print_matrix(matrix); std::cout << '\n';
print_matrix(create_sub_matrix<0>(matrix)); std::cout << '\n';
print_matrix(create_sub_matrix<1>(matrix)); std::cout << '\n';
}
Hopefully that's enough to help you implement the determinant function completely. (P.S.: No need to explicitly provide the size_t template argument to determinant when calling it, it will be automatically deduced from the size of it's std::array argument).

Make C++14 constexpr function C++11 compatible

I have written a class multi_array which is sort of an extension of std::array to multiple dimensions.
template <typename T, std::size_t... N>
class multi_array {
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
std::size_t index = 0;
using unpack = std::size_t[];
(void)unpack{0UL,
((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
meta::pack_element<I + 1, N...>::value),
0UL)...};
return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
// Storage
T m_data[meta::product<N...>::value];
//...
};
I have managed to get constexpr element access but only in C++14. The problem is the function linearized_index. It computes the linearized index at compile-time. In order to do so it reduces the tuple of indices and the tuple of dimension in a certain manner. For this reduction I need a local variable inside the function but this is not allowed in C++11. My environment does not permit the usage of C++14. Can I somehow rewrite this function to work with C++11?
I have prepared a full (not so minimal) example which compiles in C++14.
#include <cstddef> // std::size_t
namespace meta {
// product
template <std::size_t...>
struct product;
template <std::size_t head, std::size_t... dim>
struct product<head, dim...> {
static constexpr std::size_t const value = head * product<dim...>::value;
};
template <>
struct product<> {
static constexpr std::size_t const value = 1;
};
// pack_element
template <std::size_t index, std::size_t head, std::size_t... pack>
struct pack_element {
static_assert(index < sizeof...(pack) + 1, "index out of bounds");
static constexpr std::size_t const value =
pack_element<index - 1, pack...>::value;
};
template <std::size_t head, std::size_t... pack>
struct pack_element<0, head, pack...> {
static constexpr std::size_t const value = head;
};
// index_sequence
// https://stackoverflow.com/a/24481400
template <std::size_t... I>
struct index_sequence {};
template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
} // namespace meta
template <typename T, std::size_t... N>
class multi_array {
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
std::size_t index = 0;
using unpack = std::size_t[];
(void)unpack{0UL,
((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
meta::pack_element<I + 1, N...>::value),
0UL)...};
return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
// Storage
T m_data[meta::product<N...>::value];
public:
constexpr multi_array() {}
template <typename... U>
constexpr multi_array(U... data) : m_data{T(data)...} {}
template <typename... Idx>
constexpr T operator()(Idx... idx) const noexcept {
std::size_t index = linearized_index(
meta::make_index_sequence<sizeof...(idx) - 1>{}, idx...);
return m_data[index];
}
};
int main() {
constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
static_assert(b(1, 1) == 1, "!");
}
Live on Wandbox (C++14) and
Live on Wandbox (C++11)
The crucial part of your use of index is an iterative loop:
index = (index*a) + b
In your own C++14 solution, a trick of unpacking parameter pack is used. In C++11, you can formulate it in a recursive constexpr function:
struct mypair {
size_t a;
size_t b;
};
constexpr std::size_t foo(std::size_t init) {
return init;
}
template<class... Pair>
constexpr std::size_t foo(std::size_t init, mypair p0, Pair... ps) {
return foo((init+p0.a)*p0.b, ps...);
}
We use mypair instead of std::pair because the constructor of std::pair in C++11 is not constexpr. Then your iterative loop can be literally translated to:
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
using unpack = std::size_t[];
return foo(0, mypair{unpack{std::size_t(idx)...}[I], meta::pack_element<I+1, N...>::value}...) + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
Live Demo
If in operator(), instead of calling
std::size_t index = linearized_index(
meta::make_index_sequence<sizeof...(idx) - 1>{}, idx...);
you call
std::size_t index = linearized_index<N...>(idx...);
or better (to make operator() constexpr)
return m_data[linearized_index<N...>(idx...)];
you can rewrite linearized_index() recursively as follows
// ground case
template <std::size_t>
constexpr std::size_t linearized_index (std::size_t idx0) const
{ return idx0; }
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * meta::product<Is...>::value
+ linearized_index<Is...>(idxs...); }
If you prefer, the ground case can be written as follows
template <typename = void>
constexpr std::size_t linearized_index () const
{ return 0; }
Observe that you don't need meta::index_sequence, meta::make_index_sequence or meta::pack_element anymore.
The following is a full C++11 compiling example
#include <cstddef> // std::size_t
namespace meta
{
template <std::size_t...>
struct product;
template <std::size_t head, std::size_t... dim>
struct product<head, dim...>
{ static constexpr std::size_t const value
= head * product<dim...>::value; };
template <>
struct product<>
{ static constexpr std::size_t const value = 1; };
} // namespace meta
template <typename T, std::size_t... N>
class multi_array
{
private:
// ground case
template <std::size_t>
constexpr std::size_t linearized_index (std::size_t idx0) const
{ return idx0; }
// alternative ground case
//template <typename = void>
//constexpr std::size_t linearized_index () const
// { return 0; }
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * meta::product<Is...>::value
+ linearized_index<Is...>(idxs...); }
// Storage
T m_data[meta::product<N...>::value];
public:
constexpr multi_array()
{ }
template <typename ... U>
constexpr multi_array(U ... data) : m_data{T(data)...}
{ }
template <typename... Idx>
constexpr T operator() (Idx... idx) const noexcept
{ return m_data[linearized_index<N...>(idx...)]; }
};
int main()
{
constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
static_assert( b(1, 1) == 1, "!" );
constexpr multi_array<double, 4, 3, 2> const c
{ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 1};
static_assert( c(3, 2, 1) == 1, "!" );
static_assert( c(2, 1, 0) == 2, "!" );
}
Bonus suggestion: if you add the following constexpr functions (static methods inside multi_array?)
constexpr static std::size_t prod ()
{ return 1U; }
template <typename ... Args>
constexpr static std::size_t prod (std::size_t v, Args ... vs)
{ return v * prod(vs...); }
you can delete the struct product calling
// Storage
T m_data[prod(N...)];
and
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * prod(Is...) + linearized_index<Is...>(idxs...); }
Straightforward approach avoiding arrays:
#include <tuple>
#include <type_traits>
#include <cstddef>
template
<
typename x_IndexTypesTuple
, ::std::size_t ... x_dimension
> class
t_MultiIndexImpl;
template
<
typename x_LeadingIndex
, typename ... x_Index
, ::std::size_t x_leadding_dimension
, ::std::size_t ... x_dimension
> class
t_MultiIndexImpl
<
::std::tuple<x_LeadingIndex, x_Index ...>, x_leadding_dimension, x_dimension ...
> final
{
static_assert
(
::std::is_same<::std::size_t, x_LeadingIndex>::value
, "index type must be ::std::size_t"
);
public: static constexpr auto
Op
(
::std::size_t const stride_size
, x_LeadingIndex const leading_index
, x_Index const ... index
) -> ::std::size_t
{
return stride_size * leading_index
+ t_MultiIndexImpl<::std::tuple<x_Index ...>, x_dimension ...>::Op
(
stride_size * x_leadding_dimension, index ...
);
}
};
template<> class
t_MultiIndexImpl<::std::tuple<>> final
{
public: static constexpr auto
Op(::std::size_t const /*stride_size*/) -> ::std::size_t
{
return ::std::size_t{0};
}
};
template<::std::size_t ... x_dimension, typename ... x_Index> inline constexpr auto
Caclculate_MultiIndex(x_Index const ... index) -> ::std::size_t
{
static_assert
(
sizeof...(x_dimension) == sizeof...(x_Index)
, "arguments count must match dimensions count"
);
return t_MultiIndexImpl<::std::tuple<x_Index ...>, x_dimension ...>::Op(::std::size_t{1}, index ...);
}
online compiler |
godbolt
I have managed to get a C++11 compatible solution by rewriting the function to evaluate recursively. This not only works but is also a lot nicer to read:
template <typename... Idx>
constexpr std::size_t linearized_index(std::size_t n, Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[n] +
(n == 0 ? 0
: unpack{std::size_t(N)...}[n] *
linearized_index(n - 1, idx...));
}
I have found another solution using template specializations which avoids the recursive function call and replaces it with recursive instantiation.
namespace meta {
template <size_t n, size_t... N>
struct linearized_index {
template <typename... Idx>
constexpr std::size_t operator()(Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[n] +
unpack{std::size_t(N)...}[n] *
linearized_index<n - 1, N...>{}(idx...);
}
};
template <size_t... N>
struct linearized_index<0, N...> {
template <typename... Idx>
constexpr std::size_t operator()(Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[0];
}
};
} // namespace meta
and the multi_array call operator
template <typename... Idx>
constexpr T operator()(Idx... idx) const noexcept {
return m_data[meta::linearized_index<sizeof...(idx) - 1, N...>{}(
idx...)];
}
This produces perfect assembly: https://godbolt.org/g/8LPkBZ

How to extract params from template function parameter pack?

I am implementing a variadic template function because I want to make calls with up to 7 params. My calls go like this.
foo(1, 2, "msg", 4, 5.0);
or
foo(3, 4.1, "msg");
The first parameter identifies a protocol to use, and every parameter thereafter is what I would like to place in struct Msg.
struct Msg {
int proto;
string str;
int a;
int b;
double d;
};
The biggest problem I'm having is, I don't know how to get the remaining parameters after the first and store them. I'd like to use the first param to tell which struct members are to be populated. The part that confuses me is that each recursive call changes the function sig.
template<typename T>
T bar(T t) {
cout << __PRETTY_FUNCTION__ << endl;
return t;
}
template<typename T, typename... Args>
void foo(T value, Args... args)
{
cout << __PRETTY_FUNCTION__ << endl;
Msg msg;
msg.proto = value;
switch (value) {
case PROTO_A:
// when calling 'foo(1, 2, "msg", 4, 5.0)'
// 1 is proto and placed in struct Msg (msg.proto = value)
// but how to get the remaining params from args into struct Msg
foo(args...);
break;
case PROTO_B:
foo(args...);
break;
default:
break;
}
send_msg(msg);
}
You may use it without recursion:
Your specific fill method, with un-call fallback (using SFINAE and priority):
struct overload_priority_low {};
struct overload_priority_high : overload_priority_low {};
template<typename... Args>
auto Fill_ProtoA(Msg& msg, overload_priority_high, Args... args)
-> decltype(std::tie(msg.a, msg.str, msg.b, msg.d) = std::tie(args...), void())
{
std::tie(msg.a, msg.str, msg.b, msg.d) = std::tie(args...);
}
template<typename... Args> auto Fill_ProtoA(Msg& msg, overload_priority_low, Args... args)
{
throw std::runtime_error("Should not be called");
}
template<typename... Args>
auto Fill_ProtoB(Msg& msg, overload_priority_high, Args&&...args)
-> decltype(std::tie(msg.d, msg.str) = std::tie(args...), void())
{
std::tie(msg.d, msg.str) = std::tie(args...);
msg.a = 42;
msg.b = 42;
}
template<typename... Args> auto Fill_ProtoB(Msg& msg, overload_priority_low, Args... args)
{
throw std::runtime_error("Should not be called");
}
And then your dispatcher foo:
template<typename T, typename... Args>
void foo(T value, Args... args)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
Msg msg;
msg.proto = value;
switch (value) {
case PROTO_A: Fill_ProtoA(msg, overload_priority_high{}, args...); break;
case PROTO_B: Fill_ProtoB(msg, overload_priority_high{}, args...); break;
default: break;
}
send_msg(msg);
}
Demo
If I have correctly understood your problem, you want to build a variadic template function for which:
first parameter is a protocol number
following parameters can be of different types and aliment a struct
So I assume that you already have defined one struct Msg (depending on T ?) and that you are able to write the function that aliments the struct with one single param using the proto from the struct:
template <typename Arg>
void process(struct Msg& msg, Arg value) {
switch(msg.proto) {
case PROTO_A:
...
}
}
(maybe process(struct Msg<T>& msg, ...) but I leave that for you because you did not say how you process the individual parameters...
You can then write a recursive process version:
template <typename First first, typename... Args>
void process(struct Msg& msg, First first, Args ... args) {
process(msg, first);
if (sizeof...(args) > 0) {
process(msg, args...);
}
}
And your foo function can become:
template<typename T, typename... Args>
void foo(T value, Args... args)
{
cout << __PRETTY_FUNCTION__ << endl;
Msg msg;
msg.proto = value;
process(msg, args);
send_msg(msg);
}
Here's a generalization that might be of use to you. Here the arguments passed can be in any order (even empty). The compiler will deduce which arguments are assigned to which data members of the object in question (if at all).
#include <iostream>
#include <tuple>
#include <string>
constexpr std::size_t NOT_FOUND = -1;
using default_tuple = std::tuple<int, bool, double, char, std::string>;
template <typename, std::size_t> struct Pair;
template <typename Tuple, typename T, std::size_t Start, typename = void>
struct Find : std::integral_constant<std::size_t, NOT_FOUND> {};
template <typename Tuple, typename T, std::size_t Start>
struct Find<Tuple, T, Start, std::enable_if_t<(Start < std::tuple_size<Tuple>::value)>> {
static constexpr size_t value = std::is_same<std::remove_reference_t<T>, std::tuple_element_t<Start, Tuple>>::value ? Start : Find<Tuple, T, Start+1>::value;
};
template <typename T, typename... Pairs> struct SearchPairs;
template <typename T>
struct SearchPairs<T> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename First, typename... Rest>
struct SearchPairs<T, First, Rest...> : SearchPairs<T, Rest...> {};
template <typename T, std::size_t I, typename... Rest>
struct SearchPairs<T, Pair<T,I>, Rest...> : std::integral_constant<std::size_t, I> {};
template <typename Tuple, typename ArgsTuple, std::size_t Start, std::size_t OriginalSize, typename Indices, typename LastIndices, typename = void>
struct ObtainIndices {
using type = Indices;
};
template <typename Tuple1, typename Tuple2, std::size_t Start, std::size_t OriginalSize, std::size_t... Is, typename... Pairs>
struct ObtainIndices<Tuple1, Tuple2, Start, OriginalSize, std::index_sequence<Is...>, std::tuple<Pairs...>,
std::enable_if_t<(Start < std::tuple_size<Tuple2>::value)>> {
using T = std::tuple_element_t<Start, Tuple2>;
static constexpr std::size_t start = SearchPairs<T, Pairs...>::value, // Searching through Pairs..., and will be 0 only if T is not found among the pairs. Else we start after where the last T was found in Tuple1.
index = Find<Tuple1, T, start>::value;
using type = std::conditional_t<(index < OriginalSize),
typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index+1>, Pairs...>>::type, // Pair<T, index+1> because we start searching for T again (if ever) after position 'index'. Also, we must place Pair<T, index+1> before the Pairs... pack rather than after it because if a Pair with T already exists, that Pair must not be used again.
typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index>, Pairs...>>::type // We add Pair<T, index> right before Pairs... since we want to use the default T value again if another T value is ever needed. Now this could clutter up the std::tuple<Pairs...> pack with many Pairs, causing more compile time, but there is no guarantee that Pair<T, index+1> is already there (since this default T value could be the first T value found).
>;
};
template <std::size_t I, std::size_t J, typename Tuple1, typename Tuple2>
void assignHelper (Tuple1&& tuple1, const Tuple2& tuple2) {
if (std::get<J>(tuple2) != std::tuple_element_t<J, Tuple2>()) // Make the assignment only if the right hand side is not the default value.
std::get<I>(std::forward<Tuple1>(tuple1)) = std::get<J>(tuple2);
}
template <typename Tuple1, typename Tuple2, std::size_t... Is, std::size_t... Js>
void assign (Tuple1&& tuple1, Tuple2&& tuple2, std::index_sequence<Is...>, std::index_sequence<Js...>) {
const int a[] = {(assignHelper<Is, Js>(tuple1, tuple2), 0)...};
static_cast<void>(a);
}
template <typename T, typename... Args>
void fillData (T& t, Args&&... args) {
auto s = t.tuple_ref();
std::tuple<Args...> a = std::tie(args...);
const auto tuple = std::tuple_cat(a, default_tuple{}); // Add default values for each type, in case they are needed if those types are absent in 'a'.
using IndexSequence = typename ObtainIndices<std::remove_const_t<decltype(tuple)>, decltype(s), 0, sizeof...(Args), std::index_sequence<>, std::tuple<>>::type;
assign (s, tuple, std::make_index_sequence<std::tuple_size<decltype(s)>::value>{}, IndexSequence{});
}
// Testing
class Thing {
int a, b;
char c;
double d;
std::string s;
int n;
public:
auto tuple_ref() {return std::tie(a, b, c, d, s, n);} // This (non-const) member function must be defined for any class that wants to be used in the 'fillData' function. Here 'auto' is std::tuple<int&, int&, char&, double&, std::string&, int&>.
void print() const {std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << ", s = " << s << ", n = " << n << '\n';}
};
int main() {
Thing thing;
fillData (thing, 5, 12.8);
thing.print(); // a = 5, b = uninitialized, c = uninitialized, d = 12.8, s = uninitialized, n = uninitialized
fillData (thing, 3.14, 2, 'p', std::string("hi"), 5, 'k', 5.8, 10, std::string("bye"), 9); // Note that std::string("hi") must be use instead of "hi" because "hi" is of type const char[2], not std::string (then the program would not compile). See my thread http://stackoverflow.com/questions/36223914/stdstring-type-lost-in-tuple/36224017#36224006
thing.print(); // a = 2, b = 5, c = p, d = 3.14, s = hi, n = 10
fillData (thing, 4.8, 8, 'q', std::string("hello"));
thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = hello, n = 10
fillData (thing, std::string("game over"));
thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = game over, n = 10
}

constexpr initialization of array to sort contents

This is a bit of a puzzle rather than a real-world problem, but I've gotten into a situation where I want to be able to write something that behaves exactly like
template<int N>
struct SortMyElements {
int data[N];
template<typename... TT>
SortMyElements(TT... tt) : data{ tt... }
{
std::sort(data, data+N);
}
};
int main() {
SortMyElements<5> se(1,4,2,5,3);
int se_reference[5] = {1,2,3,4,5};
assert(memcmp(se.data, se_reference, sizeof se.data) == 0);
}
except that I want the SortMyElements constructor to be constexpr.
Obviously this is possible for fixed N; for example, I can specialize
template<>
struct SortMyElements<1> {
int data[1];
constexpr SortMyElements(int x) : data{ x } {}
};
template<>
struct SortMyElements<2> {
int data[2];
constexpr SortMyElements(int x, int y) : data{ x>y?y:x, x>y?x:y } {}
};
But how do I generalize this into something that will work for any N?
Please notice that the array elements have to come from the actual values of the arguments, not from template non-type arguments; my elements come from constexpr expressions that, despite being evaluated at compile-time, reside firmly inside the "value system", rather than the "type system". (For example, Boost.MPL's sort works strictly within the "type system".)
I've posted a working "answer", but it's too inefficient to work for N > 6. I'd like to use this with 2 < N < 50 or thereabouts.
(P.S.— Actually what I'd really like to do is shuffle all the zeroes in an array to the end of the array and pack the nonzero values toward the front, which might be easier than full-on sorting; but I figure sorting is easier to describe. Feel free to tackle the "shuffle zeroes" problem instead of sorting.)
It's ugly, and probably not the best way to sort in a constant expression (because of the required instantiation depth).. but voilà, a merge sort:
Helper type, returnable array type with constexpr element access:
#include <cstddef>
#include <iterator>
#include <type_traits>
template<class T, std::size_t N>
struct c_array
{
T arr[N];
constexpr T const& operator[](std::size_t p) const
{ return arr[p]; }
constexpr T const* begin() const
{ return arr+0; }
constexpr T const* end() const
{ return arr+N; }
};
template<class T>
struct c_array<T, 0> {};
append function for that array type:
template<std::size_t... Is>
struct seq {};
template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...> {};
template<class T, std::size_t N, class U, std::size_t... Is>
constexpr c_array<T, N+1> append_impl(c_array<T, N> const& p, U const& e,
seq<Is...>)
{
return {{p[Is]..., e}};
}
template<class T, std::size_t N, class U>
constexpr c_array<T, N+1> append(c_array<T, N> const& p, U const& e)
{
return append_impl(p, e, gen_seq<N>{});
}
Merge sort:
template<std::size_t Res, class T, class It, std::size_t Accum,
class = typename std::enable_if<Res!=Accum, void>::type >
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1,
c_array<T, Accum> const& accum)
{
return
beg0 == end0 ? c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1)) :
beg1 == end1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0)) :
*beg0 < *beg1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0))
: c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1));
}
template<std::size_t Res, class T, class It, class... Dummies>
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1,
c_array<T, Res> const& accum, Dummies...)
{
return accum;
}
template<class T, std::size_t L, std::size_t R>
constexpr c_array<T, L+R> c_merge(c_array<T, L> const& l,
c_array<T, R> const& r)
{
return c_merge<L+R>(l.begin(), l.end(), r.begin(), r.end(),
c_array<T, 0>{});
}
template<class T>
using rem_ref = typename std::remove_reference<T>::type;
template<std::size_t dist>
struct helper
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, dist>
{
return c_merge(helper<dist/2>::merge_sort(beg, beg+dist/2),
helper<dist-dist/2>::merge_sort(beg+dist/2, end));
}
};
template<>
struct helper<0>
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, 0>
{
return {};
}
};
template<>
struct helper<1>
{
template < class It >
static constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, 1>
{
return {*beg};
}
};
template < std::size_t dist, class It >
constexpr auto merge_sort(It beg, It end)
-> c_array<rem_ref<decltype(*beg)>, dist>
{
return helper<dist>::merge_sort(beg, end);
}
Helpers for usage example:
template<class T, std::size_t N>
constexpr std::size_t array_size(T (&arr)[N]) { return N; }
template<class T, std::size_t N>
constexpr T* c_begin(T (&arr)[N]) { return arr; }
template<class T, std::size_t N>
constexpr T* c_end(T (&arr)[N]) { return arr+N; }
Usage example:
constexpr int unsorted[] = {5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements
constexpr auto sorted = merge_sort<array_size(unsorted)>(c_begin(unsorted),
c_end(unsorted));
#include <iostream>
int main()
{
std::cout << "unsorted: ";
for(auto const& e : unsorted) std::cout << e << ", ";
std::cout << '\n';
std::cout << "sorted: ";
for(auto const& e : sorted) std::cout << e << ", ";
std::cout << '\n';
}
Output:
unsorted: 5, 7, 3, 4, 1, 8, 2, 9, 0, 6, 10,
sorted: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
I know that this is an old question but as we have C++14 (and C++17 soon), and since C++14 constexpr rules aren't so restricted, and, for sure, couple of people will find your question in google, here is how quicksort (and of course other algorithms) can be done since C++14.
(big credits to #dyp for constexpr array)
#include <utility>
#include <cstdlib>
template<class T>
constexpr void swap(T& l, T& r)
{
T tmp = std::move(l);
l = std::move(r);
r = std::move(tmp);
}
template <typename T, size_t N>
struct array
{
constexpr T& operator[](size_t i)
{
return arr[i];
}
constexpr const T& operator[](size_t i) const
{
return arr[i];
}
constexpr const T* begin() const
{
return arr;
}
constexpr const T* end() const
{
return arr + N;
}
T arr[N];
};
template <typename T, size_t N>
constexpr void sort_impl(array<T, N> &array, size_t left, size_t right)
{
if (left < right)
{
size_t m = left;
for (size_t i = left + 1; i<right; i++)
if (array[i]<array[left])
swap(array[++m], array[i]);
swap(array[left], array[m]);
sort_impl(array, left, m);
sort_impl(array, m + 1, right);
}
}
template <typename T, size_t N>
constexpr array<T, N> sort(array<T, N> array)
{
auto sorted = array;
sort_impl(sorted, 0, N);
return sorted;
}
constexpr array<int, 11> unsorted{5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements
constexpr auto sorted = sort(unsorted);
#include <iostream>
int main()
{
std::cout << "unsorted: ";
for(auto const& e : unsorted)
std::cout << e << ", ";
std::cout << '\n';
std::cout << "sorted: ";
for(auto const& e : sorted)
std::cout << e << ", ";
std::cout << '\n';
}
LIVE DEMO
A bit late to the party, but a much better and simpler implementation is the following comb_sort implementation.
template<typename Array>
constexpr void comb_sort_impl ( Array & array_ ) noexcept {
using size_type = typename Array::size_type;
size_type gap = array_.size ( );
bool swapped = false;
while ( ( gap > size_type { 1 } ) or swapped ) {
if ( gap > size_type { 1 } ) {
gap = static_cast<size_type> ( gap / 1.247330950103979 );
}
swapped = false;
for ( size_type i = size_type { 0 }; gap + i < static_cast<size_type> ( array_.size ( ) ); ++i ) {
if ( array_ [ i ] > array_ [ i + gap ] ) {
auto swap = array_ [ i ];
array_ [ i ] = array_ [ i + gap ];
array_ [ i + gap ] = swap;
swapped = true;
}
}
}
}
template<typename Array>
constexpr Array sort ( Array array_ ) noexcept {
auto sorted = array_;
comb_sort_impl ( sorted );
return sorted;
}
int main ( ) {
constexpr auto sorted = sort ( std::array<int, 8> { 6, 8, 0, 1, 5, 9, 2, 7 } );
for ( auto i : sorted )
std::cout << i << ' ';
std::cout << std::endl;
return EXIT_SUCCESS;
}
Output: 0 1 2 5 6 7 8 9
Why better, it's [the algorithm] often as good as insertion sort, but is non-recursive, which means it will work on any size arrays (at least not limited by the recursive depth).
Well, I got my inefficient version to compile, at least with Clang on OSX. Here's the code.
However, while it's tolerably fast for five elements, on my laptop it takes 0.5 seconds to sort six elements and 7 seconds to sort seven elements. (Catastrophically varying performance, too, depending on whether the items are almost-sorted or reverse-sorted.) I didn't even try timing eight. Clearly, this doesn't scale to the kind of things I want to do with it. (I'd say 50 elements is a reasonable upper bound for my contrived use-case, but 6 is unreasonably tiny.)
#include <cstring>
#include <cassert>
template<int...>
struct IntHolder {};
// Now let's make a consecutive range of ints from [A to B).
template<int A, int B, int... Accum>
struct IntRange_ : IntRange_<A+1, B, Accum..., A> {};
template<int A, int... Accum>
struct IntRange_<A, A, Accum...> {
using type = IntHolder<Accum...>;
};
template<int A, int B>
using IntRange = typename IntRange_<A,B>::type;
// And a helper function to do what std::min should be doing for us.
template<typename... TT> constexpr int min(TT...);
constexpr int min(int i) { return i; }
template<typename... TT> constexpr int min(int i, TT... tt) { return i < min(tt...) ? i : min(tt...); }
template<int N>
struct SortMyElements {
int data[N];
template<int... II, typename... TT>
constexpr SortMyElements(IntHolder<II...> ii, int minval, int a, TT... tt) : data{
( a==minval ? a : SortMyElements<N>(ii, minval, tt..., a).data[0] ),
( a==minval ? SortMyElements<N-1>(tt...).data[II] : SortMyElements<N>(ii, minval, tt..., a).data[II+1] )...
} {}
template<typename... TT>
constexpr SortMyElements(TT... tt) : SortMyElements(IntRange<0,sizeof...(tt)-1>(), min(tt...), tt...) {}
};
template<>
struct SortMyElements<1> {
int data[1];
constexpr SortMyElements(int x) : data{ x } {}
constexpr SortMyElements(IntHolder<>, int minval, int x) : SortMyElements(x) {}
};
static_assert(SortMyElements<5>(5,2,1,3,1).data[0] == 1, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[1] == 1, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[2] == 2, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[3] == 3, "");
static_assert(SortMyElements<5>(5,2,1,3,1).data[4] == 5, "");
char global_array[ SortMyElements<5>(1,4,2,5,3).data[2] ];
static_assert(sizeof global_array == 3, "");
int main() {
SortMyElements<5> se(1,4,2,5,3);
int se_reference[5] = {1,2,3,4,5};
assert(memcmp(se.data, se_reference, sizeof se.data) == 0);
}
UPDATE: I haven't figured out how to do a fast mergesort (although DyP's answer looks potentially feasible to me). However, this morning I did solve my original puzzle-problem of shuffling zeroes to the end of an array! I used a recursive partition-and-merge algorithm; the code looks like this.
Starting with C++20, all you need to change in your example is to add constexpr to the constructor. That is, in C++20, std::sort is in fact constexpr.