I try to learn a little bit about template metaprogramming and
currently i play around with variadic templates.
In his talk "Variadic Templates are Funadic" Alexandrescu introduces a
small tuple implementation, which i try to build and maybe extend a
little bit. (I know it is a toy example, i just try to learn a little
bit more about c++). However, i have a small problem with his code.
Here it is:
template <typename... Ts>
class tuple
{};
template<size_t, typename> struct tuple_element;
template<typename T, typename... Ts>
struct tuple_element<0, tuple<T, Ts...>>
{
typedef T type;
};
template <size_t k, typename T, typename... Ts>
struct tuple_element<k, tuple<T, Ts...>>
{
typedef
typename tuple_element<k-1,tuple<Ts...>>::type type;
};
template<size_t k, typename... Ts>
typename std::enable_if<k == 0,
typename tuple_element<0,tuple<Ts...>>::type&>::type
get(tuple<Ts...>& t)
{return t.head_;}
template<size_t k, typename T, typename... Ts>
typename std::enable_if<k != 0,
typename tuple_element<k,tuple<T,Ts...>>::type&>::type
get(tuple<T,Ts...>& t)
{
tuple<Ts...> & super = t;
return get<k-1>(super);
}
template <typename T, typename... Ts>
class tuple<T,Ts...> : private tuple<Ts...>
{
private:
T head_;
};
int main(int argc, char *argv[])
{
tuple<int,std::string> t;
get<0>(t) = 10;
get<1>(t) = std::string("test");
std::cout<<get<0>(t)<<std::endl;
}
In order to work correctly, the get function must be friend of the
tuple class (It is also mentioned on this slides, see 32). But how
does the friend declaration looks like? I tried different approaches
but could not get it to work. When i change the code from private to public inheritance
and change the access rules for head_ to public it works.
Thanks for your help
Kevin
This works for me:
template <typename T, typename... Ts>
class tuple<T,Ts...> : private tuple<Ts...>
{
private:
T head_;
template<size_t k, typename T1, typename... T1s>
friend typename std::enable_if<k != 0,
typename tuple_element<k,tuple<T1,T1s...>>::type&>::type
get(tuple<T1,T1s...>& t);
template<size_t k, typename... T1s>
friend typename std::enable_if<k == 0,
typename tuple_element<0,tuple<T1s...>>::type&>::type
get(tuple<T1s...>& t);
};
Demo.
Another implementation from the other point of view:
#include <iostream>
#include <type_traits>
template <class... Args>
class Tuple;
template <>
class Tuple<> {};
template <class T, class... Args>
class Tuple<T, Args...>: public Tuple<Args...> {
using Base = Tuple<Args...>;
T Value_;
public:
Tuple(T&& value, Args&&... args)
: Value_(std::forward<T>(value))
, Base(std::forward<Args>(args)...)
{
}
T& Value() {
return Value_;
}
};
template <size_t k, class T, class... Args>
struct Select {
using Type = typename Select<k - 1, Args...>::Type;
};
template <class T, class... Args>
struct Select<0, T, Args...> {
using Type = T;
};
template <size_t k, class... Args>
using TSelect = typename Select<k, Args...>::Type;
template <bool P, class T>
using TEnableIf = typename std::enable_if<P, T>::type;
template <size_t k, class T, class... Args>
TEnableIf<(k != 0), TSelect<k, T, Args...>&> get(Tuple<T, Args...>& t) {
return get<k - 1, Args...>(t);
}
template <size_t k, class T, class... Args>
TEnableIf<(k == 0), TSelect<k, T, Args...>&> get(Tuple<T, Args...>& t) {
return t.Value();
}
int main() {
Tuple<int, char> t(1, 'a');
std::cout << get<0>(t) << std::endl;
std::cout << get<1>(t) << std::endl;
get<1>(t) = 'b';
std::cout << get<1>(t) << std::endl;
}
Actually, we don't need a Tuple to get a type.
Related
I am actually thinking of something similar to the '*' operator in python like this:
args = [1,2,4]
f(*args)
Is there a similar solution in C++?
What I can come up with is as follows:
template <size_t num_args, typename FuncType>
struct unpack_caller;
template <typename FuncType>
struct unpack_caller<3>
{
void operator () (FuncType &f, std::vector<int> &args){
f(args[0], args[1], args[3])
}
};
Above I assume only int argument type.
The problem is that I feel it is a hassle to write all the specializations of unpack_caller for different value of num_args.
Any good solution to this? Thanks.
You can use a pack of indices:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
void call(FuncType &f, std::vector<int> &args, indices<I...>){
f(args[I]...);
}
public:
template <typename FuncType>
void operator () (FuncType &f, std::vector<int> &args){
assert(args.size() == num_args); // just to be sure
call(f, args, BuildIndices<num_args>{});
}
};
There's no way to remove the need to specify the size in the template though, because the size of a vector is a runtime construct, and we need the size at compile-time.
Update to #Fernandes's answer.
Yes, there does be a way to remove the need to specifying num_args in template parameter. This is because num_args is determined by the function signature, not the vector. What should be checked at run-time is the size of the vector and the arity of the function.
See the following example.
#include <iostream>
#include <utility>
#include <vector>
#include <cassert>
namespace util {
template <typename ReturnType, typename... Args>
struct function_traits_defs {
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
template <size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename T>
struct function_traits_impl;
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
template <size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <size_t N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <size_t N>
using BuildIndices = typename build_indices<N>::type;
namespace details {
template <typename FuncType,
typename VecType,
size_t... I,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT do_call(FuncType& func,
VecType& args,
indices<I...> ) {
assert(args.size() >= Traits::arity);
return func(args[I]...);
}
} // namespace details
template <typename FuncType,
typename VecType,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT unpack_caller(FuncType& func,
VecType& args) {
return details::do_call(func, args, BuildIndices<Traits::arity>());
}
} // namespace util
int func(int a, int b, int c) {
return a + b + c;
}
int main() {
std::vector<int> args = {1, 2, 3};
int j = util::unpack_caller(func, args);
std::cout << j << std::endl;
return 0;
}
If you use c++14 there is update to #r-martinho-fernandes response using index-sequence:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
void call(FuncType &f, std::vector<int> &args, std::index_sequence<I...>){
f(args[I]...);
}
public:
template <typename FuncType>
void operator () (FuncType &f, std::vector<int> &args){
assert(args.size() == num_args); // just to be sure
call(f, args, std::make_index_sequence<num_args>{});
}
};
Is there a utility in the standard library to get the index of a given type in std::variant? Or should I make one for myself? That is, I want to get the index of B in std::variant<A, B, C> and have that return 1.
There is std::variant_alternative for the opposite operation. Of course, there could be many same types on std::variant's list, so this operation is not a bijection, but it isn't a problem for me (I can have first occurrence of type on list, or unique types on std::variant list).
Update a few years later: My answer here may be a cool answer, but this is the correct one. That is how I would solve this problem today.
We could take advantage of the fact that index() almost already does the right thing.
We can't arbitrarily create instances of various types - we wouldn't know how to do it, and arbitrary types might not be literal types. But we can create instances of specific types that we know about:
template <typename> struct tag { }; // <== this one IS literal
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };
That is, to find the index of B in variant<A, B, C> we construct a variant<tag<A>, tag<B>, tag<C>> with a tag<B> and find its index.
This only works with distinct types.
I found this answer for tuple and slightly modificated it:
template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t variant_index() {
static_assert(std::variant_size_v<VariantType> > index, "Type not found in variant");
if constexpr (index == std::variant_size_v<VariantType>) {
return index;
} else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
return index;
} else {
return variant_index<VariantType, T, index + 1>();
}
}
It works for me, but now I'm curious how to do it in old way without constexpr if, as a structure.
You can also do this with a fold expression:
template <typename T, typename... Ts>
constexpr size_t get_index(std::variant<Ts...> const&) {
size_t r = 0;
auto test = [&](bool b){
if (!b) ++r;
return b;
};
(test(std::is_same_v<T,Ts>) || ...);
return r;
}
The fold expression stops the first time we match a type, at which point we stop incrementing r. This works even with duplicate types. If a type is not found, the size is returned. This could be easily changed to not return in this case if that's preferable, since missing return in a constexpr function is ill-formed.
If you dont want to take an instance of variant, the argument here could instead be a tag<variant<Ts...>>.
With Boost.Mp11 this is a short, one-liner:
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
Full example:
#include <variant>
#include <boost/mp11/algorithm.hpp>
using namespace boost::mp11;
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
int main()
{
using V = std::variant<int,double, char, double>;
static_assert(IndexInVariant<V, int> == 0);
// for duplicates first idx is returned
static_assert(IndexInVariant<V, double> == 1);
static_assert(IndexInVariant<V, char> == 2);
// not found returns ".end()"/ or size of variant
static_assert(IndexInVariant<V, float> == 4);
// beware that const and volatile and ref are not stripped
static_assert(IndexInVariant<V, int&> == 4);
static_assert(IndexInVariant<V, const int> == 4);
static_assert(IndexInVariant<V, volatile int> == 4);
}
One fun way to do this is to take your variant<Ts...> and turn it into a custom class hierarchy that all implement a particular static member function with a different result that you can query.
In other words, given variant<A, B, C>, create a hierarchy that looks like:
struct base_A {
static integral_constant<int, 0> get(tag<A>);
};
struct base_B {
static integral_constant<int, 1> get(tag<B>);
};
struct base_C {
static integral_constant<int, 2> get(tag<C>);
};
struct getter : base_A, base_B, base_C {
using base_A::get, base_B::get, base_C::get;
};
And then, decltype(getter::get(tag<T>())) is the index (or doesn't compile). Hopefully that makes sense.
In real code, the above becomes:
template <typename T> struct tag { };
template <std::size_t I, typename T>
struct base {
static std::integral_constant<size_t, I> get(tag<T>);
};
template <typename S, typename... Ts>
struct getter_impl;
template <std::size_t... Is, typename... Ts>
struct getter_impl<std::index_sequence<Is...>, Ts...>
: base<Is, Ts>...
{
using base<Is, Ts>::get...;
};
template <typename... Ts>
struct getter : getter_impl<std::index_sequence_for<Ts...>, Ts...>
{ };
And once you establish a getter, actually using it is much more straightforward:
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: decltype(getter<Ts...>::get(tag<T>()))
{ };
That only works in the case where the types are distinct. If you need it to work with independent types, then the best you can do is probably a linear search?
template <typename T, typename>
struct get_index;
template <size_t I, typename... Ts>
struct get_index_impl
{ };
template <size_t I, typename T, typename... Ts>
struct get_index_impl<I, T, T, Ts...>
: std::integral_constant<size_t, I>
{ };
template <size_t I, typename T, typename U, typename... Ts>
struct get_index_impl<I, T, U, Ts...>
: get_index_impl<I+1, T, Ts...>
{ };
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: get_index_impl<0, T, Ts...>
{ };
My two cents solutions:
template <typename T, typename... Ts>
constexpr std::size_t variant_index_impl(std::variant<Ts...>**)
{
std::size_t i = 0; ((!std::is_same_v<T, Ts> && ++i) && ...); return i;
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T>(static_cast<V**>(nullptr));
template <typename T, typename V, std::size_t... Is>
constexpr std::size_t variant_index_impl(std::index_sequence<Is...>)
{
return ((std::is_same_v<T, std::variant_alternative_t<Is, V>> * Is) + ...);
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T, V>(std::make_index_sequence<std::variant_size_v<V>>{});
If you wish a hard error on lookups of not containing type or duplicate type - here are static asserts:
constexpr auto occurrences = (std::is_same_v<T, Ts> + ...);
static_assert(occurrences != 0, "The variant cannot have the type");
static_assert(occurrences <= 1, "The variant has duplicates of the type");
Another take on it:
#include <type_traits>
namespace detail {
struct count_index {
std::size_t value = 0;
bool found = false;
template <typename T, typename U>
constexpr count_index operator+(const std::is_same<T, U> &rhs)
{
if (found)
return *this;
return { value + !rhs, rhs};
}
};
}
template <typename Seq, typename T>
struct index_of;
template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
And then:
#include <variant>
struct A{};
struct B{};
struct C{};
using V = std::variant<A, B, C>;
static_assert(index_of<V, B>::value == 1);
Or:
static_assert(index_of<std::tuple<int, float, bool>, float>::value == 1);
See on godbolt: https://godbolt.org/z/7ob6veWGr
What is the problem with this?
struct foo {
void process(int, char, bool) {}
};
foo myfoo;
template <typename Method> struct thing {
void doit() {
Method m = Method{};
(myfoo.*m)(5, 'a', true);
}
};
int main() {
thing<decltype(&foo::process)> t;
t.doit();
}
I think this isolates the problem. What is the workaround if I have to use the type Method, as in the case of my original post below?
Original post:
In the following attempted test:
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
As you can predict, func is supposed to carry out
foo.play('c', true); bar.jump(5, 2, 4.5); baz.run(6.8);
My implementation of the Functor class so far (ignoring perfect forwarding and such for now) is
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
where my last line using std::invoke is just to test the concept before I continue. It however will not compile on either GCC 7.2 or clang 6.0, so I cannot continue with the generalization. Any workaround here, or a completely different implementation altogether?
Here is everything I have so far:
namespace utilities {
template <std::size_t N, typename... Ts>
struct nth_element : std::tuple_element<N, std::tuple<Ts...>> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
}
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs, typename... Members> struct many_members_h;
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs>
struct many_members_h<Rs, Ts, ArgsPacks, AllArgs> {
using return_types = Rs;
using classes = Ts;
using args_packs = ArgsPacks;
using all_args = AllArgs;
};
template <typename... Rs, typename... Ts, typename... ArgsPacks, typename... AllArgs, typename R, typename T, typename... Args, typename... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Ts...>, std::tuple<ArgsPacks...>, std::tuple<AllArgs...>, R(T::*)(Args...), Rest...> :
many_members_h<std::tuple<Rs..., R>, std::tuple<Ts..., T>, std::tuple<ArgsPacks..., std::tuple<Args...>>, std::tuple<AllArgs..., Args...>, Rest...> { };
template <typename... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
// Testing
#include <iostream>
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
Taking your smaller first example, note that decltype(&foo::process) is the type called void (foo::*)(int, char, bool).
This type does not contain or imply any association with the original function foo::process itself. Just like the type int doesn't let you get the value of some particular int elsewhere in your program, or the type SomeClass doesn't let you refer to a SomeClass object elsewhere in your program, the type alone doesn't carry a value or identity.
The expression Method{} value-initializes this pointer to member type. Which means the resulting value is a null pointer value. Which means calling it is undefined behavior (and on many systems is likely to result in a segfault).
If you're using C++17 mode, you could use a template <auto Method> non-type parameter and simply pass &foo::process (without using decltype) as the template argument. Some SFINAE techniques could enforce that the argument is actually a pointer to member function, and some helper traits could be used to get the class type and parameter list tuple.
Or if you're using a standard earlier than C++17, you'll have to either make the function pointer a function argument, or make it a template parameter which follows the type, as in template <typename MethodType, MethodType Method>, then call as thing<decltype(&foo::process), &foo::process>.
Thanks to aschepler's answer and advice to use auto... instead of typename... for the member function pointers, I was able to carry the original goal:
#include <tuple>
#include <functional> // std::invoke
#include <type_traits>
#include <utility>
namespace utilities {
template <std::size_t N, auto I, auto... Is>
struct nth_element : nth_element<N - 1, Is...> { };
template <auto I, auto... Is>
struct nth_element<0, I, Is...> {
static constexpr decltype(I) value = I;
};
template <std::size_t N, typename Pack> struct nth_index;
template <std::size_t N, std::size_t... Is>
struct nth_index<N, std::index_sequence<Is...>> : nth_element<N, Is...> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
template <typename F, typename T, typename Tuple, std::size_t... Is>
decltype(auto) invoke_with_tuple_h (F&& f, T&& t, Tuple&& tuple, std::index_sequence<Is...>&&) {
return std::invoke(std::forward<F>(f), std::forward<T>(t), std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename T, typename Tuple>
decltype(auto) invoke_with_tuple (F&& f, T&& t, Tuple&& tuple) {
return invoke_with_tuple_h (std::forward<F>(f), std::forward<T>(t), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
template <typename PartialSums, std::size_t Sum, std::size_t... Is> struct all_partial_sums_h;
template <std::size_t... PartialSums, std::size_t Sum>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum> {
using type = std::index_sequence<PartialSums..., Sum>;
using type_without_last_sum = std::index_sequence<PartialSums...>; // We define this because this is what we need actually.
};
template <std::size_t... PartialSums, std::size_t Sum, std::size_t First, std::size_t... Rest>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum, First, Rest...> :
all_partial_sums_h<std::index_sequence<PartialSums..., Sum>, Sum + First, Rest...> { };
template <typename Pack> struct all_partial_sums;
template <std::size_t... Is>
struct all_partial_sums<std::index_sequence<Is...>> : all_partial_sums_h<std::index_sequence<>, 0, Is...> { };
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
template <typename PackOfPacks> struct get_pack_sizes;
template <template <typename...> class P, typename... Packs>
struct get_pack_sizes<P<Packs...>> {
using type = std::index_sequence<pack_size<Packs>::value...>;
};
}
template <typename Method> struct method_traits;
template <typename R, typename C, typename... Args>
struct method_traits<R(C::*)(Args...)> {
using return_type = R;
using class_type = C;
using args_type = std::tuple<Args...>;
};
template <typename Rs, typename Cs, typename ArgsPacks, auto... Members> struct many_members_h;
template <typename Rs, typename Cs, typename ArgsPacks>
struct many_members_h<Rs, Cs, ArgsPacks> {
using return_types = Rs;
using classes = Cs;
using args_packs = ArgsPacks;
};
template <typename... Rs, typename... Cs, typename... ArgsPacks, auto F, auto... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Cs...>, std::tuple<ArgsPacks...>, F, Rest...> :
many_members_h<std::tuple<Rs..., typename method_traits<decltype(F)>::return_type>, std::tuple<Cs..., typename method_traits<decltype(F)>::class_type>, std::tuple<ArgsPacks..., typename method_traits<decltype(F)>::args_type>, Rest...> { };
template <auto... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <auto... Members>
struct Functor {
using m = many_members<Members...>;
using starting_points = typename utilities::all_partial_sums<typename utilities::get_pack_sizes<typename m::args_packs>::type>::type;
template <typename... Args>
typename m::return_types operator()(Args&&... args) const {
constexpr std::size_t M = sizeof...(Members);
auto t = std::make_tuple(std::forward<Args>(args)...);
auto objects = utilities::tuple_head<M>(t);
auto arguments = utilities::extract_subtuple<M, sizeof...(Args) - M>(t);
return call(objects, arguments, std::make_index_sequence<M>{});
}
private:
template <typename Tuple1, typename Tuple2, std::size_t... Is>
auto call (Tuple1& objects, const Tuple2& args, std::index_sequence<Is...>&&) const { // perfect forwarding to do later
return std::make_tuple(call_helper<Is>(objects, args)...);
}
template <std::size_t N, typename Tuple1, typename Tuple2>
auto call_helper (Tuple1& objects, const Tuple2& args) const { // perfect forwarding to do later
constexpr std::size_t s = std::tuple_size_v<std::tuple_element_t<N, typename m::args_packs>>;;
constexpr std::size_t a = utilities::nth_index<N, starting_points>::value;
const auto args_tuple = utilities::extract_subtuple<a, s>(args);
return utilities::invoke_with_tuple (utilities::nth_element<N, Members...>::value, std::get<N>(objects), args_tuple);
}
};
// Testing
#include <iostream>
struct Foo { int play (char c, bool b) { std::cout << std::boolalpha << "Foo::play(" << c << ", " << b << ") called.\n"; return 3; } };
struct Bar { double jump (int a, short b, float c) { std::cout << "Bar::jump(" << a << ", " << b << ", " << c << ") called.\n"; return 5.8; } };
struct Baz { char run (double d) { std::cout << "Baz::run(" << d << ") called.\n"; return 'b'; } };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<&Foo::play, &Bar::jump, &Baz::run> func;
const auto tuple = func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
std::cin.get();
}
Output:
Baz::run(6.8) called.
Bar::jump(5, 2, 4.5) called.
Foo::play(c, true) called.
I'm trying to implement an std::tuple through variadic template with recursive inheritance and external get function. I works well as long as tuple has public inheritance and public value field. But i need to make them private and to accomplish it i need to write a friend "get" function in the tuple. But the problem is that returned type of "get" is calculated through other variadic template. So I don't know what to write as returned value of that friend function.
#include <iostream>
#include <type_traits>
template <typename... Ts>
class Tuple{};
template <typename T, typename... Ts>
class Tuple<T, Ts...> : public Tuple<Ts...>
{
// template<size_t k, typename T1, typename... T1s>
// friend typename tuple_element<k, tuple<T1, T1s...>>::type&
// get(Tuple<T1, T1s...>& t);
public:
T m_head;
};
template<size_t index, typename>
struct tuple_element;
template<typename T, typename... Ts>
struct tuple_element<0, Tuple<T, Ts...>>
{
typedef T type;
};
template<size_t k, typename T, typename... Ts>
struct tuple_element<k, Tuple<T, Ts...>>
{
typedef typename tuple_element<k-1, Tuple<Ts...>>::type type;
};
template<size_t k, typename... Ts>
typename std::enable_if<k==0,
typename tuple_element<0, Tuple<Ts...>>::type&>::type
get(Tuple<Ts...>& t)
{
return t.m_head;
}
template<size_t k, typename T, typename... Ts>
typename std::enable_if<k!=0,
typename tuple_element<k, Tuple<T, Ts...>>::type&>::type
get(Tuple<T, Ts...>& t)
{
Tuple<Ts...>& super = t;
return get<k-1>(super);
}
int main(int argc, char* argv[])
{
Tuple<int, int, std::string> t;
get<2>(t) = "3.14";
std::cout << get<2>(t) << std::endl;
}
Commented code was my attempt to write such function but it doesn't work.
Forward-declare tuple_element and then make friends from the two overloaded get functions:
template <size_t index, typename>
struct tuple_element;
template <typename T, typename... Ts>
class Tuple<T, Ts...> : Tuple<Ts...>
{
template <size_t k, typename... Us>
friend typename std::enable_if<k==0, typename tuple_element<0, Tuple<Us...>>::type&>::type get(Tuple<Us...>& t);
template <size_t k, typename U, typename... Us>
friend typename std::enable_if<k!=0, typename tuple_element<k, Tuple<U, Us...>>::type&>::type get(Tuple<U, Us...>& t);
private:
T m_head;
};
DEMO
template<typename... Types>
struct Foo;
template<typename T , typename... Types>
struct Foo<T, Types ...> : public Foo<Types ...>
{
Foo( T member , Types ... others ) : Foo<Types ...>( others... ), m_member( member )
{
}
T m_member;
};
template<typename T>
struct Foo<T>
{
Foo( T member ) : m_member( member )
{
}
T m_member;
};
int main()
{
Foo<char,int,bool,double> f( 'a' , 42 , true , 1.234 );
}
I found this code somewhere on SO and I am wondering if it's completely useless? It seems to me that all members are called m_member so how would I access them?
If I do cout << f.m_member; it will print 'a', but I see no way to access the other members.
In your current implementation each derived Foo class shadows its parent's m_member. It is up to you how to implement a logic of accessing each field (via indexing, types, other).
One possibility is to access them by an overloaded templated member function taking a type or an index (reversed for simplicity):
#include <type_traits>
#include <cstddef>
template <typename... Types>
struct Foo;
template <typename T, typename... Types>
struct Foo<T, Types...> : Foo<Types...>
{
// bring get() member functions from parent class into current scope
using Foo<Types...>::get;
Foo(T member, Types... others) : Foo<Types...>{others...}, m_member{member} {}
template <typename U>
auto get(T* = nullptr)
-> typename std::enable_if<std::is_same<U, T>::value, T&>::type
{
return m_member;
}
template <std::size_t N>
auto get(T* = nullptr)
-> typename std::enable_if<N == sizeof...(Types), T&>::type
{
return m_member;
}
private:
T m_member;
};
template <typename T>
struct Foo<T>
{
Foo(T member) : m_member{member} {}
template <typename U>
auto get(T* = nullptr)
-> typename std::enable_if<std::is_same<U, T>::value, T&>::type
{
return m_member;
}
template <std::size_t N>
auto get(T* = nullptr)
-> typename std::enable_if<N == 0, T&>::type
{
return m_member;
}
private:
T m_member;
};
Tests:
Foo<char, int, bool, double> a{ 'a', 42, true, 1.234 };
assert('a' == a.get<char>());
assert(42 == a.get<int>());
assert(true == a.get<1>());
assert(42 == a.get<2>());
a.get<char>() = 'b';
assert('b' == a.get<3>());
DEMO
For other implementations that provide an access to members see std::tuple<...> with its std::get<N>().
A canonical implementation looks as below:
#include <type_traits>
#include <cstddef>
template <typename... Types>
struct Foo;
template <typename T, typename... Types>
struct Foo<T, Types...> : Foo<Types...>
{
Foo(T member, Types... others) : Foo<Types...>{others...}, m_member{member} {}
T m_member;
};
template <typename T>
struct Foo<T>
{
Foo(T member) : m_member{member} {}
T m_member;
};
template <std::size_t N, typename T>
struct element;
template <typename T, typename... Types>
struct element<0, Foo<T, Types...>>
{
using type = T;
};
template <std::size_t N, typename T, typename... Types>
struct element<N, Foo<T, Types...>>
{
using type = typename element<N - 1, Foo<Types...>>::type;
};
template <std::size_t N, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<N == 0, T&>::type
{
return f.m_member;
}
template <std::size_t N, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<N != 0
, typename element<N, Foo<T, Types...>>::type&
>::type
{
Foo<Types...>& p = f;
return get<N - 1>(p);
}
template <typename U, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<std::is_same<T, U>::value, T&>::type
{
return f.m_member;
}
template <typename U, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<!std::is_same<T, U>::value, U&>::type
{
Foo<Types...>& p = f;
return get<U>(p);
}
Tests:
Foo<char, int, bool, double> a{ 'a', 42, true, 1.234 };
assert(true == get<2>(a));
assert(42 == get<int>(a));
get<char>(a) = 'b';
assert('b' == get<0>(a));
DEMO 2