Created
May 31, 2021 22:42
-
-
Save xaedes/80de5f0303c70c89f3698372d196801d to your computer and use it in GitHub Desktop.
Recursive arithmetic std::tuple example with operator+()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <tuple> | |
#include <type_traits> | |
// https://gist.github.com/ntessore/dc17769676fb3c6daa1f | |
namespace std14 | |
{ | |
template<typename T, T... Ints> | |
struct integer_sequence | |
{ | |
typedef T value_type; | |
static constexpr std::size_t size() { return sizeof...(Ints); } | |
}; | |
template<std::size_t... Ints> | |
using index_sequence = integer_sequence<std::size_t, Ints...>; | |
template<typename T, std::size_t N, T... Is> | |
struct make_integer_sequence : make_integer_sequence<T, N-1, N-1, Is...> {}; | |
template<typename T, T... Is> | |
struct make_integer_sequence<T, 0, Is...> : integer_sequence<T, Is...> {}; | |
template<std::size_t N> | |
using make_index_sequence = make_integer_sequence<std::size_t, N>; | |
template<typename... T> | |
using index_sequence_for = make_index_sequence<sizeof...(T)>; | |
} | |
//https://stackoverflow.com/a/31763111/798588 | |
template <class T, template <class...> class Template> | |
struct is_specialization : std::false_type {}; | |
template <template <class...> class Template, class... Args> | |
struct is_specialization<Template<Args...>, Template> : std::true_type {}; | |
template <class T> | |
struct Arithmetic | |
{ | |
using type = T; | |
}; | |
template <class T> | |
using Arithmetic_t = typename Arithmetic<T>::type; | |
template < | |
class T, | |
class... Args, | |
std::size_t ... Is | |
> | |
T construct_from_tuple( | |
const std::tuple<Args...>& tpl, | |
std14::index_sequence<Is...> const & | |
) | |
{ | |
return T{ std::get<Is>(tpl)... }; | |
} | |
template<class Tuple> | |
struct ArithmeticTuple : public Tuple | |
{ | |
using Tuple::Tuple; | |
ArithmeticTuple(const Tuple& tpl): Tuple(tpl) {} | |
Tuple& as_tuple() { return static_cast<Tuple&>(*this); } | |
const Tuple& as_tuple() const { return static_cast<const Tuple&>(*this); } | |
template<class T> | |
operator T() const | |
{ | |
return construct_from_tuple<T>(static_cast<const Tuple&>(*this), std14::make_index_sequence<std::tuple_size<Tuple>::value>{}); | |
} | |
template < | |
class OtherTuple, | |
std::size_t ... Is, | |
std::enable_if_t<std::tuple_size<Tuple>::value == std::tuple_size<OtherTuple>::value, bool> = true | |
> | |
static ArithmeticTuple add( | |
const ArithmeticTuple& lhs, | |
const ArithmeticTuple<OtherTuple>& rhs, | |
std14::index_sequence<Is...> const & | |
) | |
{ | |
// inspired by https://stackoverflow.com/a/47209931/798588 | |
return { | |
( | |
Arithmetic_t<typename std::tuple_element<Is, Tuple>::type>(std::get<Is>(lhs)) | |
+ | |
Arithmetic_t<typename std::tuple_element<Is, OtherTuple>::type>(std::get<Is>(rhs)) | |
)... | |
}; | |
} | |
template < | |
class T, | |
std::size_t ... Is, | |
std::enable_if_t<is_specialization<T, std::tuple>::value, bool> = true | |
> | |
static ArithmeticTuple add( | |
const ArithmeticTuple& lhs, | |
const T& rhs, | |
std14::index_sequence<Is...> const & | |
) | |
{ | |
return { | |
( | |
static_cast<Arithmetic_t<typename std::tuple_element<Is, Tuple>::type>>(std::get<Is>(lhs)) | |
+ | |
static_cast<Arithmetic_t<typename std::tuple_element<Is, T>::type>>(std::get<Is>(rhs)) | |
)... | |
}; | |
} | |
template < | |
class T, | |
std::size_t ... Is, | |
std::enable_if_t<!is_specialization<T, std::tuple>::value, bool> = true | |
> | |
static ArithmeticTuple add( | |
const ArithmeticTuple& lhs, | |
const T& rhs, | |
std14::index_sequence<Is...> const & | |
) | |
{ | |
return { | |
( | |
static_cast<Arithmetic_t<typename std::tuple_element<Is, Tuple>::type>>(std::get<Is>(lhs)) | |
+ | |
static_cast<Arithmetic_t<T>>(rhs) | |
)... | |
}; | |
} | |
}; | |
template <class...Args> struct Arithmetic<std::tuple<Args...>> { using type = ArithmeticTuple<std::tuple<Args...>>; }; | |
template < | |
class Tuple, | |
class Rhs | |
> | |
ArithmeticTuple<Tuple> operator+ ( | |
const ArithmeticTuple<Tuple> & lhs, | |
const Rhs & rhs | |
) | |
{ | |
return ArithmeticTuple<Tuple>::add(lhs, rhs, std14::make_index_sequence<std::tuple_size<Tuple>::value>{}); | |
} | |
template < | |
class Tuple, | |
class Lhs, | |
std::enable_if_t<is_specialization<Lhs, ArithmeticTuple>::value == false, bool> = true | |
> | |
ArithmeticTuple<Tuple> operator+ ( | |
const Lhs & lhs, | |
const ArithmeticTuple<Tuple> & rhs | |
) | |
{ | |
return rhs + lhs; | |
} | |
struct Foo | |
{ | |
float a; | |
int b; | |
using tuple_type = std::tuple<float, int>; | |
tuple_type as_tuple() const { return {a, b}; } | |
operator tuple_type() const { return {a, b}; } | |
}; | |
template <> struct Arithmetic<Foo> { using type = ArithmeticTuple<Foo::tuple_type>; }; | |
int main () | |
{ | |
ArithmeticTuple<std::tuple<int,double>> t0; | |
ArithmeticTuple<std::tuple<int,double>> t1 = std::make_tuple(1,2); | |
ArithmeticTuple<std::tuple<float,int>> t2 = std::make_tuple(3,4); | |
auto t3 = t1 + t1; | |
auto t4 = t2 + t2; | |
auto t5 = t1 + t2 + 10; | |
auto t6 = -10 + t5; | |
std::cout << std::get<0>(t1) << " " << std::get<1>(t1) << std::endl; | |
std::cout << std::get<0>(t2) << " " << std::get<1>(t2) << std::endl; | |
std::cout << std::get<0>(t3) << " " << std::get<1>(t2) << std::endl; | |
std::cout << std::get<0>(t4) << " " << std::get<1>(t3) << std::endl; | |
std::cout << std::get<0>(t5) << " " << std::get<1>(t5) << std::endl; | |
std::cout << std::get<0>(t6) << " " << std::get<1>(t6) << std::endl; | |
ArithmeticTuple<std::tuple<float,std::tuple<int,double>>> t7 = std::make_tuple(5,std::make_tuple(6,7)); | |
ArithmeticTuple<std::tuple<float,std::tuple<float,double>>> t8 = std::make_tuple(8,std::make_tuple(9.5,10)); | |
std::cout << std::get<0>(t7) << " " << std::get<0>(std::get<1>(t7)) << " " << std::get<1>(std::get<1>(t7)) << std::endl; | |
std::cout << std::get<0>(t8) << " " << std::get<0>(std::get<1>(t8)) << " " << std::get<1>(std::get<1>(t8)) << std::endl; | |
auto t9 = t7+t8; | |
std::cout << std::get<0>(t9) << " " << std::get<0>(std::get<1>(t9)) << " " << std::get<1>(std::get<1>(t9)) << std::endl; | |
t2 = t1; | |
std::cout << std::get<0>(t1) << " " << std::get<1>(t1) << std::endl; | |
std::cout << std::get<0>(t2) << " " << std::get<1>(t2) << std::endl; | |
std::tuple<int,double> tpl1 = t1; | |
std::cout << std::get<0>(tpl1) << " " << std::get<1>(tpl1) << std::endl; | |
Foo foo{20,21}; | |
std::cout << std::get<0>(foo.as_tuple()) << " " << std::get<1>(foo.as_tuple()) << std::endl; | |
Arithmetic_t<Foo> foo_(foo); | |
foo_ = foo_+foo_; | |
std::cout << std::get<0>(foo_) << " " << std::get<1>(foo_) << std::endl; | |
Foo bar = foo_; | |
std::cout << std::get<0>(bar.as_tuple()) << " " << std::get<1>(bar.as_tuple()) << std::endl; | |
} | |
/* | |
Output: | |
1 2 | |
3 4 | |
2 4 | |
6 4 | |
14 16 | |
4 6 | |
5 6 7 | |
8 9.5 10 | |
13 15 17 | |
1 2 | |
1 2 | |
1 2 | |
20 21 | |
40 42 | |
40 42 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment