Related
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.
I'm now learning a little about templates and templates in C++11, C++14 and C++1z. I'm trying to write a variadic class template with an inside class that will associate an int to every template argument - and have a constexpr method that returns its array representation.
Let's say that I have ensured that the template cannot receive two of the same type as an argument. I was thinking about doing it somewhat like this:
template <typename... Types>
struct MyVariadicTemplate {
//we know that all types in Types... are different
template <int... Values>
struct MyInnerTemplate {
//I need to make sure that sizeof...(Values) == sizeof...(Types)
constexpr std::array<int, sizeof...(Values)> to_array() {
std::array<int, sizeof...(Values)> result = {Values...};
return result;
// this is only valid since C++14, as far as I know
}
};
};
this code should be valid (if it's not, I'd love to know why). Now, I'd like to add another inner template:
template <typedef Type>
struct AnotherInnerTemplate {};
that has a public typedef, which represents MyInnerTemplate with one on the position of Type in Types... and zeros elsewhere - and here I'm lost. I don't know how to proceed
I would appreciate any hint on how that can be done - and if I'm heading towards the wrong direction, I hope somebody can give me a hint on how to do that.
I think what you're looking for is something like this.
#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
template <typename NeedleT, typename... HaystackTs>
constexpr auto get_type_index_mask() noexcept
{
constexpr auto N = sizeof...(HaystackTs);
return std::array<bool, N> {
(std::is_same<NeedleT, HaystackTs>::value)...
};
}
template <typename T, std::size_t N>
constexpr std::size_t ffs(const std::array<T, N>& array) noexcept
{
for (auto i = std::size_t {}; i < N; ++i)
{
if (array[i])
return i;
}
return N;
}
int
main()
{
const auto mask = get_type_index_mask<float, bool, int, float, double, char>();
for (const auto& bit : mask)
std::cout << bit;
std::cout << "\n";
std::cout << "float has index " << ffs(mask) << "\n";
}
Output:
00100
float has index 2
The magic happens in the parameter pack expansion
(std::is_same<NeedleT, HaystackTs>::value)...
where you test each type in HaystackTs against NeedleT. You might want to apply std::decay to either type if you want to consider, say, const int and int the same type.
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
Full:
template <class... Types> struct My {
template <int... Values> struct Inner {
constexpr std::array<int, sizeof...(Values)> to_array() {
return std::array<int, sizeof...(Values)>{Values...};
}
};
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
};
auto main() -> int {
My<int, float, char>::Another<int>::Type s;
auto a = s.to_array();
for (auto e : a) {
cout << e << " ";
}
cout << endl;
return 0;
}
prints:
1 0 0
Is this what you want?
Consider this illegal code:
template <int... Is>
struct Object {
void foo() const;
};
template <int... Js>
void Object<0, Js...>::foo() {/*Do whatever*/}
We want to specialize foo() when the first template parameter is 0, and let's say we want to specialize foo() as well if the second parameter is 3, and the third int is 1. So the solution I found (not sure if its the best approach) is the following:
#include <iostream>
template <int...> struct Foo;
template <int... Is>
struct Object {
int ID; // This member is just to illustrate the case when 'this' is needed in foo().
friend struct Foo<Is...>;
void foo() const {Foo<Is...>::execute(this);} // Pass 'this' in case it is needed.
};
template <int... Is>
struct Foo<0, Is...> {
static void execute (const Object<0, Is...>* object) {std::cout << "First int = 0, ID = " << object->ID << ".\n";}
};
template <int N, int... Is>
struct Foo<N, 3, Is...> {
static void execute (const Object<N, 3, Is...>* object) {std::cout << "Second int = 3, ID = " << object->ID << ".\n";}
};
template <int M, int N, int... Is>
struct Foo<M, N, 1, Is...> {
static void execute (const Object<M, N, 1, Is...>* object) {std::cout << "Third int = 1, ID = " << object->ID << ".\n";}
};
int main() {
Object<0,5,8,2>{4}.foo();
Object<4,3,2,5,3>{2}.foo();
Object<4,2,1>{0}.foo();
}
First of all, is this solution any good? Next, the problem now arises if we try Object<0,3,1,4>{8}.foo(); because the spec was not complete. So let's say that the earliest matched specialized int will always take precedence. So in this case Object<0,3,1,4>{8}.foo(); should run the first specialization because of the 0, while Object<9,3,1,4>{8}.foo(); shall run the second specialization because of the 3, and so forth. How to enforce that rule?
I suggest just using if statements. The compiler will probably optimize them away anyway (assuming you have optimization enabled).
In other words, just do something like this:
template <int... Js>
void Object::foo() {
std::array<int, sizeof...(Js)> args = {Js...}; // I _think_ this is the correct syntax to dump the parameter pack into an std::array.
if(args.size() > 0 && args[0] == 0) {
// First argument is 0, do whatever.
} else {
// It's not 0, do your other thing.
}
}
You'll get pretty much the same effect, and your code will be quite a bit clearer.
A comment and a hint.
The approach for me is OK. Since we do not have partial template specialization for functions that's all we have.
Then regarding Object<0,3,1,4>{8}.foo() this gives ambiguous partial specializations (on Clang 3.6). To solver this problem I ended up adding another partial specialization
template <int... Is>
struct Foo<0, 3, Is...> {
static void execute (const Object<0, 3, Is...>* object) {std::cout << "First int = 0, second = 3, ID = " << object->ID << ".\n";}
};
Another possibility is mess with std::integer_sequence. I have to give up now, the following is not a solution, just an appetizer...
#include <utility>
#include <iostream>
template <class S1, class S2>
struct seq_lt
{
enum {value = 0} ;
} ;
template <int I1, int ...S1, int I2, int ...S2>
struct seq_lt<std::integer_sequence<int, I1, S1...>,
std::integer_sequence<int, I2, S2...>>
{
enum {value = (I1 < I2 ? 1 : 0)} ;
} ;
int main(int argc, char *argv[])
{
std::integer_sequence<int, 1, 2, 3> seq1 ;
std::integer_sequence<int, 2, 3> seq2 ;
std::cout << "seq_lt " << seq_lt<decltype(seq1), decltype(seq2)>::value << std::endl ;
std::cout << "seq_lt " << seq_lt<decltype(seq2), decltype(seq1)>::value << std::endl ;
}
This solution was inspired by Marom's second suggestion, and also inspired partly by celticminstrel's solution too.
#include <iostream>
#include <type_traits>
template <std::size_t, typename T, T...> struct NthValue;
template <typename T, T First, T... Rest>
struct NthValue<0, T, First, Rest...> : std::integral_constant<T, First> {};
template <std::size_t N, typename T, T First, T... Rest>
struct NthValue<N, T, First, Rest...> : NthValue<N - 1, T, Rest...> {};
template <int... Is>
struct Object {
void foo() const {fooHelper (typename Map<Is...>::type{});}
private:
template <int...> struct Map;
template <int, int> struct MappedType {};
struct Default {};
void fooHelper (const MappedType<0,0>&) const {std::cout << "First int = 0.\n";}
void fooHelper (const MappedType<1,3>&) const {std::cout << "Second int = 3.\n";}
void fooHelper (const MappedType<2,1>&) const {std::cout << "Third int = 1.\n";}
void fooHelper (const Default&) const {std::cout << "Default case.\n";}
};
template <int... Ns>
template <int... Is>
struct Object<Ns...>::Map {
using type = typename std::conditional<NthValue<0, int, Is...>::value == 0,
MappedType<0,0>,
typename std::conditional<NthValue<1, int, Is...>::value == 3,
MappedType<1,3>,
typename std::conditional<NthValue<2, int, Is...>::value == 1,
MappedType<2,1>,
Default
>::type
>::type
>::type;
};
int main() {
Object<0,5,8,2>().foo(); // First int = 0.
Object<4,3,2,5,3>().foo(); // Second int = 3.
Object<4,2,1>().foo(); // Third int = 1.
Object<0,3,1,4>().foo(); // First int = 0.
Object<9,3,1,4>().foo(); // Second int = 3.
Object<9,9,9>().foo(); // Default case.
}
There is also no run-time overhead.
Suppose:
make_pack_indices<START, END>::type is the type pack_indices<START, START+1, START+2, ..., END-1>
make_reverse_indices<std::size_t SIZE>::type is the type pack_indices<SIZE-1, SIZE-2, ..., 2, 1, 0>
make_rotated_indices<SIZE, SHIFT>::type is the type pack_indices<SHIFT, SHIFT+1, ..., SIZE-1, 0, 1, 2, ..., SHIFT-1>
make_alternating_indices<SIZE, START, INTERVAL>::type is the type pack_indices<START, START + INTERVAL, START + 2 * INTERVAL, ...> until the largest index less than START is reached.
How do I carry out a combination of these forms of make_indices through some sort of composition like
compose<A, C, B, A..., D>::type,
where A,B,C,D are types (functors or whatever) representing the above four make_indices types? So for example,
A = make_pack_indices<1,4>
B = make_reverse_indices<5>
C = make_rotated_indices<5,2>
Then
compose<B,C,A>::type
would be
pack_indices<4,3,2,1,0> -> pack_indices<2,1,0,4,3> -> pack_indices<1,0,4>
A simpler example is that
compose <make_rotated_indices<10,2>, make_rotated_indices<10,4>>::type
would be
make_rotated_indices<10,6>::type.
Here is an application. Suppose we have the argument pack
('a', 3.14 , 5, "home", '!', 4.5, "car", 20, 0.5, 'b')
We can reverse this using my make_reverse_indices<10>, and we can rotate it to the left by 3 using make_rotated_indices<10,3>. But suppose I want to reverse the pack followed by rotating to the left by 3. Instead of manually doing one followed by the other, I want to define a compose struct that will take make_reverse_indices<10> and make_rotated_indices<10,3> as parameters and give the desired result all in one go, which would be
("car", 4.5, '!', "home", 5, 3.14, 'a', 'b', 0.5, 20)
I think (in the above example) make_reverse_indices<10> and make_rotated_indices<10,3> themselves would need to be converted to something else. make_reverse_indices<10> gives 9,8,7,...,1,0. So make_rotated_indices<10,3> (instead of giving 3,4,5,...9,0,1,2) needs to be turned into another function that accepts the parameter pack 9,8,7,...,1,0 and then carry out the effect of make_rotated_indices<10,3>, thereby giving 6,5,4,...0,9,8,7. Another idea I'm working on: define a helper function to extract the indices from rotated_indices<10,3>::type, turn that into a parameter pack and then apply make_rotated_indices<10,3> on that parameter pack.
Here is the code I already have, in case anyone needs it for testing (the structs themselves have already been tested thoroughly):
#include <iostream>
#include <string>
#include <tuple>
template<std::size_t...>
struct pack_indices
{
using type = pack_indices;
};
constexpr int positiveModulo(int i, int n)
{
return (i % n + n) % n;
}
template<std::size_t START, std::size_t END, std::size_t... INDICES>
struct make_indices : make_indices<START + 1, END, INDICES..., START>
{};
template<std::size_t END, std::size_t... INDICES>
struct make_indices<END, END, INDICES...> : pack_indices <INDICES...>
{};
template<std::size_t SIZE, std::size_t... INDICES>
struct make_reverse_indices : make_reverse_indices<SIZE - 1, INDICES..., SIZE - 1>
{};
template<std::size_t... INDICES>
struct make_reverse_indices<0, INDICES...> : pack_indices<INDICES...>
{};
template<std::size_t SIZE, std::size_t SHIFT, std::size_t SHIFT_, std::size_t... INDICES>
struct make_rotated_indices_helper
: make_rotated_indices_helper<SIZE, SHIFT, SHIFT_ + 1, INDICES..., positiveModulo(SHIFT + SHIFT_, SIZE)>
{};
template<std::size_t SIZE, std::size_t SHIFT, std::size_t...INDICES>
struct make_rotated_indices_helper<SIZE, SHIFT, SIZE, INDICES...> : pack_indices<INDICES...>
{};
template<std::size_t SIZE, std::size_t SHIFT, std::size_t... INDICES>
struct make_rotated_indices
{
using type = make_rotated_indices_helper<SIZE, SHIFT, 0, INDICES...>;
};
template<std::size_t SIZE, std::size_t START, std::size_t INTERVAL, std::size_t NUM_LEFT, std::size_t... INDICES>
struct make_alternating_indices_helper
: make_alternating_indices_helper<SIZE, START + INTERVAL, INTERVAL, NUM_LEFT - 1, INDICES..., positiveModulo(START, SIZE)>
{};
template<std::size_t SIZE, std::size_t START, std::size_t INTERVAL, std::size_t... INDICES>
struct make_alternating_indices_helper<SIZE, START, INTERVAL, 0, INDICES...>
: pack_indices<INDICES...>
{};
template<std::size_t SIZE, std::size_t START, std::size_t INTERVAL, std::size_t... INDICES>
struct make_alternating_indices
{
using type = make_alternating_indices_helper<SIZE, START, INTERVAL, (SIZE - 1) / INTERVAL + 1>;
};
// Testing
namespace Pack
{
template<typename LAST>
void print(LAST && last)
{
std::cout << std::forward<LAST>(last) << std::endl;
}
template<typename FIRST, typename... REST>
void print(FIRST && first, REST&&... rest)
{
std::cout << std::forward<FIRST>(first) << ", ";
print<REST...>(std::forward<REST>(rest)...);
}
}
template<typename TUPLE, std::size_t... INDICES>
void showValuesHelper(TUPLE&& tuple,
const pack_indices<INDICES...>&)
{
Pack::print(std::get<INDICES>(std::forward<TUPLE>(tuple))...);
}
template<std::size_t START, std::size_t END, typename... TYPES>
void showMiddleValues(TYPES && ...types)
{
const auto tuple = std::forward_as_tuple(std::forward<TYPES>(types)...);
const typename make_indices<START, END>::type indices;
showValuesHelper (tuple, indices);
}
int main()
{
std::cout << "original argument pack: ";
Pack::print('a', 3.14, 5, "home", '!', 4.5, "car", 20, 0.5, 'b');
std::cout << "showMiddleValues<2,7> = ";
showMiddleValues<2, 7>('a', 3.14, 5, "home", '!', 4.5, "car", 20, 0.5, 'b'); // 5, home, !, 4.5, car
}
One posible solution could be using simple recursion and the binary concatenation:
template<typename LHS , typename RHS>
struct concat;
template<std::size_t... Is , std::size_t... Js>
struct concat<sequence<Is...>,sequence<Js...>>
{
using type = sequence<Is...,Js...>;
};
template<typename... Ss>
struct multi_concat;
template<typename HEAD , typename... TAIL>
struct multi_concat<HEAD,TAIL...>
{
using type = typename concat<HEAD,typename multi_concat<TAIL...>::type>::type;
};
template<>
struct multi_concat<>
{
using type = sequence<>;
};
This is just a start of what I'm trying to achieve. It is not generalized because it is being carried out only for rotation as an illustration (and it is self-composition instead of general composition of arbitrarily many functions):
template <int N, std::size_t SHIFT, std::size_t... INDICES>
struct rotateAgain;
template <int N, std::size_t SHIFT, typename... TYPES> // N is the number of times rotation by SHIFT is being done
void rotatePack (TYPES&&... types) {
const typename make_rotated_indices<sizeof...(TYPES), SHIFT>::type indices;
rotateAgain<N-1, SHIFT>()(indices, types...);
}
template <int N, std::size_t SHIFT, std::size_t... INDICES>
struct rotateAgain {
template <typename... TYPES>
void operator() (const pack_indices<INDICES...>&, const TYPES&... types) const {
const typename make_rotated_indices<sizeof...(TYPES), SHIFT>::type indices;
Pack::print (Pack::get<INDICES>(types...)...);
rotatePack<N, SHIFT> (Pack::get<INDICES>(types...)...);
}
};
template <std::size_t SHIFT, std::size_t... INDICES>
struct rotateAgain<0, SHIFT, INDICES...> {
template <typename... TYPES>
void operator() (const pack_indices<INDICES...>&, const TYPES&... types) const {
std::cout << "End of recursion" << std::endl;
}
};
There must be some way to generalize this from make_rotated_indices (which is being used above) to any of the make_indices types defined.
Another day, another experiment with template meta-programming that's gone awry. I'm attempting to make an is_greater_than template that will take in two integral values N and M of type T.
template<typename T, T N, T M>
struct is_greater_than<void, N, M>;
template<typename T = std::enable_if<std::is_integral<T>::value, T>::value, T N, T M>
struct is_greater_than<T, N, M>
{
static const bool value = N > M;
};
Try as I might I can't seem to get this to work. Attempting to compile this yields 112 compiler errors. I have an ideone fiddle here: http://ideone.com/ch1j7b.
What am I doing wrong here? Any help would be appreciated!
Your usage of std::enable_if is wrong, it should be a separate template parameter (possibly unnamed):
#include <iostream>
#include <type_traits>
template<typename T, T N, T M,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
struct is_greater_than:
public std::integral_constant<bool, (N > M)>::type
{
};
int main()
{
std::cout << is_greater_than<int, 1, 2>::value
<< is_greater_than<int, 1, 1>::value
<< is_greater_than<int, 2, 1>::value;
}
Note that I'm inheriting fromstd::integral_constant here, it'll define the value and type members for us based in its second argument (parenthesis around N > M are required).
Try this:
template<typename T, T N, T M, typename enable = void>
struct is_greater_than;
template<typename T, T N, T M>
struct is_greater_than<T,N,M,
typename std::enable_if<std::is_integral<T>::value>::type>
{
static const bool value = N > M;
};
int main()
{
bool a = is_greater_than<int, 11, 10>::value;
cout << boolalpha << a << endl;
}
Output:
true
Live code
This compiles fine in VS2010:
template<typename T, T M, T N>
struct is_greater_than;
template<typename T = std::enable_if< std::is_integral<T>::value, T >::value, T M = T(), T N = T()>
struct is_greater_than {
static const bool value = M > N;
};
You can test it with:
std::cout << is_greater_than<int,4,2>::value << std::endl;
std::cout << is_greater_than<std::string,"a","B">::value << std::endl;