Modifying types of tuple elements - c++

Having a list of types as a variadic template argument, it's pretty easy to perform arbitrary type manipulation on them, to get a tuple of modified types as a result. E.g. to wrap each element with a custom wrapper class, one could do:
template<typename T> class Wrapper {};
template<typename ...Values>
using WrappedValues = std::tuple<Wrapper<Values>...>;
using NewTuple = WrappedValues<int, std::string, char>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
How to do the same, when having a specialization of std::tuple as an "input"? E.g. what should be placed instead of "???" to make following code compileable:
template<typename T> class Wrapper {};
template<typename Tuple>
using WrappedTupleElements = ???;
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
I know about the possibility to access types of tuple elements using std::tuple_element and recursive template instantiation, but I don't know how to gather types created this way into one tuple.
Preferred would be pure C++14 answer, but proposals that use C++17, widely available TSes, or external libraries (e.g. boost) are also welcome.

Why not to use additional struct template which allow specialization:
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
template<typename Tuple>
struct WrappedTupleElements;
template <class... Values>
struct WrappedTupleElements<std::tuple<Values...>> {
using type = std::tuple<Wrapper<Values>...>;
};
int main() {
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>::type;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}
[live demo]

A common idiom (e.g. boost mpl) is to employ the concept of metafunctions.
A metafunction is a template class which declares a type called result which yields the result type of applying the metafunction on the inputs.
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
namespace metafunction
{
template<class MetaFunction> using result_of = typename MetaFunction::result;
// meta-function which yields Wrapper<Element> from Element
// type: unary metafunction
// arg1 = the type to wrap
// returns Wrapper<arg1>
//
template<class Element>
struct apply_wrapper
{
using result = Wrapper<Element>;
};
template<class Tuple, template<class> class Function>
struct transform_elements;
// meta-function which takes a tuple and a unary metafunction
// and yields a tuple of the result of applying the metafunction
// to each element_type of the tuple.
// type: binary metafunction
// arg1 = the tuple of types to be wrapped
// arg2 = the unary metafunction to apply to each element_type
// returns tuple<result_of<arg2<element>>...> for each element in arg1
template<class...Elements, template<class> class UnaryMetaFunction>
struct transform_elements<std::tuple<Elements...>, UnaryMetaFunction>
{
template<class Arg> using function = UnaryMetaFunction<Arg>;
using result = std::tuple
<
result_of<function<Elements>>...
>;
};
}
int main() {
using input_type = std::tuple<int, std::string, char>;
using namespace metafunction;
using wrapped_tuple = result_of<transform_elements<input_type, apply_wrapper>>;
static_assert(std::is_same<wrapped_tuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}

Related

defining floating point constants based on deduced type

I have a function template that takes in a 3D vector (anything that has a bracket operator and three elements: MyCustom3DVector, float[3], double*, etc.) and computes something from it. The signature looks like that:
template <typename Vec3>
constexpr auto compute_stuff(const Vec3& v);
In this function, I need to define the constant 1/5. The issue is that the underlying type could be either float or double. I somehow need to extract the underlying type of my Vec3. I can do so using
using T = std::remove_reference_t<decltype(std::declval<Vec3>()[0])>;
constexpr auto oneFifth = T{1.0/5.0};
but this looks ugly and unnecessarily complicated. Is there a better way to proceed?
I would write a type trait for this.
Assuming you want to support pointers to first element of an array, containers that do have a value_type and a limited number of containers that do not have a value_type member alias:
#include <type_traits>
#include <iostream>
#include <vector>
template <typename T,typename = void> struct value_type;
// either its a pointer
template <typename T> struct value_type<T*,void> { using type = T; };
// or it has value_type
template <typename T> struct value_type<T,std::void_t<typename T::value_type>> { using type = typename T::value_type;};
// for convenience
template <typename T> using value_type_t = typename value_type<T>::type;
// ..or it doesn't have a value_type member alias
// some custom container
struct my_vector {
// no value_type
double* data;
};
// specialization for custom container
template <> struct value_type<my_vector> { using type = double;};
int main()
{
std::cout << std::is_same_v< int,value_type_t<std::vector<int>>>;
std::cout << std::is_same_v<double,value_type_t<double*>>;
std::cout << std::is_same_v<double,value_type_t<my_vector>>;
// oneFifth
value_type_t<my_vector> oneFifth{1.0/5.0};
}

Is it possible to generate a variant from an integer sequence?

Would it be possible to generate a variant from an integer sequence like in this pseudo C++ sample:
#include <utility>
#include <variant>
template <int MyInt>
struct MyStruct{};
using MyIntegerSequence = std::make_integer_sequence<int, 3>;
using MyVariant = std::variant<MyStruct<MyIntegerSequence>...>; // Does not compile
int main()
{
return sizeof(MyVariant);
}
On coliru: http://coliru.stacked-crooked.com/a/2439048b107642c2
It's possible, sure. The key is that one must use a pack expansion of some sort. Your struct takes an int non-type template parameter. So we must expand the integer sequence's pack to feed them into MyStruct one at a time.
MyStruct<MyIntegerSequence>... is not a pack expansion, since there is no template definition and no pack in that context.
For example, with a class template (and partial specialization):
template<typename Seq> struct MyVariantHelper;
template<int... Is> struct MyVariantHelper<std::integer_sequence<int, Is...>> {
using type = std::variant<MyStruct<Is>...>;
};
using MyVariant = MyVariantHelper<MyIntegerSequence>::type;
Or with a function template and decltype:
template<int... Is>
auto MyVariantHelper(std::integer_sequence<int, Is...>)
-> std::variant<MyStruct<Is>...>;
using MyVariant = decltype(MyVariantHelper(MyIntegerSequence{}));

Tuples as templates in c++

I get a tuple of types (e.g. std::tuple) and I want to create tuple of vectors given by these types. I wrote this, but when I use it as I want, it doesn't work, I get this error:
Error (active) E0730 type "types" is not a class template
I am pretty new to variadic templates so I don't know how to do it properly.
using namespace std;
template <template <typename...> typename Tuple, typename... Types>
class vertices
{
public:
tuple<vector<Types...> > v;
};
int main{
using types = tuple<int, string, double>;
vertices<types> ver;
}
In your code, Tuple is a template template parameter, so it expects a template. types is not a template but a concrete type, so it is not usable. Instead what you can do is just take in the tuple, and then using a helper meta function get the type for the member like
// no definition since it is only used in unevaluated contexts, just used as a helper to get the type converted
template <typename... Types>
std::tuple<std::vector<Types>...> tuple_to_tuple_of_vectors(std::tuple<Types...>);
template <typename Tuple>
class vertices
{
public:
using tuple_type = decltype(tuple_to_tuple_of_vectors(std::declval<Tuple>()));
tuple_type v;
};
int main ()
{
using types = tuple<int, string, double>;
vertices<types> ver;
}
From your question, it appears your requirement is just to be able to conveniently express the type
tuple<vector<int>, vector<string>, vector<double>>
as
vertices<tuple<int, string, double>>
This can be achieved using a variable template. All we need is to take a type (that's a tuple), and unpack that tuple into a vector. Since a type is not a variadic pack, we need another level of indirection to get the types within the tuple. #NathanOliver's answer shows a nice way to do that, using the declaration of a function-template. As pointed out, since all we need is type transformations, the function doesn't need a definition, the declaration says it all: the argument type is the input type, and the return type is the output type.
template <typename... Types>
auto unpack(tuple<Types...>) -> tuple<vector<Types>...> ;
template <typename Tuple>
using vertices = decltype(unpack(declval<Tuple>()));
static_assert(std::is_same<
vertices<tuple<int, string, double>>,
tuple<vector<int>, vector<string>, vector<double>>>{});
Your first template parameter is a template template parameter, but types is not a template, it is a type. Also you have the ... in the wrong place. You need tuple<vector<Types>...>, otherwise Types is expanded as parameters for vector.
The following comes with a disclaimer: I know a bit of tempaltes with C++11, but I am rather ignorant about newer features, so there might be more elegant ways to write the same.
As base template you can use:
template <typename... Types>
struct vertices
{
using tuple_of_vectors_t = tuple<vector<Types>... >;
tuple_of_vectors_t v;
};
And then for tuples:
template <typename... Types>
struct vertices<std::tuple<Types...>> : vertices<Types...> {};
Complete example:
#include <tuple>
#include <vector>
#include <iostream>
#include <type_traits>
using namespace std;
template <typename... Types>
struct vertices
{
using tuple_of_vectors_t = tuple<vector<Types>... >;
tuple_of_vectors_t v;
};
template <typename... Types>
struct vertices<std::tuple<Types...>> : vertices<Types...> {};
int main () {
using types = tuple<int, string, double>;
vertices<types> ver;
std::cout << std::is_same_v< vertices<types>::tuple_of_vectors_t,
std::tuple< std::vector<int>,
std::vector<std::string>,
std::vector<double>
>
>;
}
Output:
1

Get function arguments type as tuple

Problem
Given any function (or callable) type Function, how can I get its all arguments types as a tuple type ?
For example, I need a trait function_traits<Function>::arguments, where:
int f();
typename function_traits<decltype(f)>::arguments // => gives me std::tuple<>
void g(int);
typename function_traits<decltype(g)>::arguments // => gives me std::tuple<int>
void h(int, int);
typename function_traits<decltype(h)>::arguments // => gives me std::tuple<int, int>
My thought
First
I need to get the size of arguments, fortunately boost already has implemented function_traits<F>::arity
Then
Generate an std::integer_sequence from 1 to artify, map it to arguments type, but here comes the problem, to map integer_sequence, I need something like this:
function_traits<F>::arg_type<N> // -> N-th arg_type
but boost only provides this:
function_traits<F>::argN_type
Question
How can I implement function_traits<F>::arg_type<N> ? I can use c++ standard up to c++17
Something like this:
#include <tuple>
template<typename x_Function> class
function_traits;
// specialization for functions
template<typename x_Result, typename... x_Args> class
function_traits<x_Result (x_Args...)>
{
public: using arguments = ::std::tuple<x_Args...>;
};
usage example:
#include <type_traits>
int foo(int);
using foo_arguments = function_traits<decltype(foo)>::arguments;
static_assert(1 == ::std::tuple_size<foo_arguments>::value);
static_assert(::std::is_same_v<int, ::std::tuple_element<0, foo_arguments>::type>);
online compiler
Is too late to play?
You can use C++17 so... what about using std::function deduction guides?
template <typename T>
struct function_traits
{
template <typename R, typename ... As>
static std::tuple<As...> pro_args (std::function<R(As...)>);
using arguments = decltype(pro_args(std::function{std::declval<T>()}));
};
The following is a full compiling example
#include <tuple>
#include <functional>
#include <type_traits>
int f ();
void g (int);
void h (int, int);
template <typename T>
struct function_traits
{
template <typename R, typename ... As>
static std::tuple<As...> pro_args (std::function<R(As...)>);
using arguments = decltype(pro_args(std::function{std::declval<T>()}));
};
int main ()
{
static_assert(std::is_same_v<std::tuple<>,
function_traits<decltype(f)>::arguments>);
static_assert(std::is_same_v<std::tuple<int>,
function_traits<decltype(g)>::arguments>);
static_assert(std::is_same_v<std::tuple<int, int>,
function_traits<decltype(h)>::arguments>);
}

Extracting a tuple of value_type from a tuple of containers in C++11

I have a function with a template parameter which I know to be a std::tuple of several standard C++ containers of varying element types.
How can I extract, out of this, a type that is a std::tuple of the element types?
For example, suppose I have the following function
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = /*extract a tuple type by applying CtrT::value_type to each container in tupOfCtrs and combining the results into an std::tuple*/;
MyHelperClass<TupOfElements> helper;
}
and I know it is being called like this:
std::list<Foo> l {/*...*/};
std::vector<Bar> v {/*...*/};
std::deque<Baz> d {/*...*/};
auto tup = std::make_tuple(l, v, d);
In this case, I want the TupOfElements helper type to be defined as std::tuple<Foo, Bar, Baz>.
Note that I do not need to actually create the tuple, only to get its type.
How can this be achieved, possibly using the Boost::Fusion library?
You can do this even in a more simple manner without Boost Fusion like this:
// Template which takes one type argument:
template <typename Tuple> struct TupOfValueTypes;
// Only provide a definition for this template for std::tuple arguments:
// (i.e. the domain of this template metafunction is any std::tuple)
template <typename ... Ts>
struct TupOfValueTypes<std::tuple<Ts...> > {
// This definition is only valid, if all types in the tuple have a
// value_type type member, i.e. the metafunction returns a type only
// if all types of the members in the std::tuple have a value_type
// type member, and a std::tuple can be constructed from these:
using type = std::tuple<typename Ts::value_type...>;
};
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = typename TupOfValueTypes<TupOfCtrs>::type;
// ...
}
But it is of course easier to specify doStuff for the std::tuple explicitly:
template <typename ... Ts>
void doStuff(const std::tuple<Ts...> & tupOfCtrs) {
using TupOfElements = std::tuple<typename Ts::value_type...>;
// ...
}
PS: Also note, that in many cases if you need to just have a list of types, the std::tuple class is an overkill, and might slightly hurt compilation times. Personally, I've always instead used a simple TypeList struct:
template <typename ... Ts> struct TypeList
{ using type = TypeList<Ts...>; };
If you want doStuff to take a std::tuple, make that explicit:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr) { ... }
Once you have that parameter pack, it's just a matter of pulling out the value_type:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr)
{
using value_tuple = std::tuple<typename Ts::value_type...>;
// ...
}