I'm writing a templated class, and I want to have a tuple that holds some data. The type of the tuple is related to the template arguments in this way:
template<typename ...Types>
class MyClass{
public:
std::tuple<int, (repeat "int" sizeof...(Types) times)> MyData;
}
For example, MyClass<int, float, std::string, double> would result in a MyData variable of type std::tuple<int, int, int, int>. I've looked into fold expressions, but I'm not sure that they can do what I want. Is there a way to do this in C++, and if so, how?
As it says in the comments, use std::array. But, for completeness:
You can use std::conditional.
#include <string>
#include <tuple>
#include <type_traits>
template<typename ...Types>
struct TupleN { std::tuple<typename std::conditional<true, int, Types>::type...> MyData; };
static_assert(std::is_same_v<decltype(TupleN<int, float, std::string, double>::MyData),
std::tuple<int, int, int, int>>);
You could also roll your own:
template<typename T> struct Ignore { using type = int; };
Related
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
This question already has answers here:
Can I extend variant in C++?
(3 answers)
Closed 3 years ago.
#include <string>
#include <variant>
int main()
{
using variant_base = std::variant< int, double >;
using variant_derived_and_flattened = std::variant< std::string, variant_base >;
// the above does not produce the desired
// std::variant< int, double, std::string >
static_assert( std::is_same< variant_base, variant_derived_and_flattened >{} );
}
The static_assert fails.
I assume there is no way to do this and would appreciate a confirmation of this, but perhaps someone knows how to do this?
You can create a meta-function to transform the variant type.
template <class V, class T> struct variant_append_helper;
template <class... A, class T>
struct variant_append_helper<std::variant<A...>, T> {
using type = std::variant<A..., T>;
};
template <class V, class T>
using variant_append = typename variant_append_helper<V, T>::type;
static_assert(std::is_same<std::variant<int, double, std::string>,
variant_append<std::variant<int, double>, std::string>>{});
I am having a hard time understanding why the following simple program won't compile. I have a variadic template class (my_type below) which I want to use to transform an mpl vector. The following snippet leads to a compilation error "/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:38:19: 'apply' following the 'template' keyword does not refer to a template".
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
// expected result is vector< my_type<int>, my_type<double> >
using result_type = transform< test_type, my_type<_> >::type;
int main() {
}
Making my_type take a single template parameter works fine, but I would like to understand why the variadic version does not work. Thank you in advance!
The second argument expected by transform is a unary operation which you are not passing.
In your case, my_type is not a metafunction that mpl expects since it is making use of a variadic parameter list.
A metafunction in the simplest case exposes a type typedef or a ststic bool value. For eg:
template <typename T>
struct add_pointer {
using type = T*;
};
Note how add_pointer converts the provided template parameter. This is an example of unary operation since it takes only one template parameter T.
In your example my_type is purely a type and cannot be used as a metafunction or operation as it does not satisfy the criteria of the metafunction as required by the transform metafunction which is have a type field to indicate the transformed type.
In some cases, a simple temmplate type is converted into a metafunction as explained in the Detailed Reasoning section below.
Doc reference : http://www.boost.org/doc/libs/1_31_0/libs/mpl/doc/ref/Reference/transform.html
Code:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <type_traits>
#include <typeindex>
#include <iostream>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
template <typename T>
struct add_my_type {
using type = my_type<T>;
};
using result_type = typename transform< test_type, add_my_type<_1> >::type;
int main() {
static_assert (equal<result_type, vector< my_type<int>, my_type<double> >>::value, "Nope!!");
std::cout << typeid(result_type).name() << std::endl;
}
LIVE DEMO
Detailed Reason
The reason explained above is pretty brief in should be enough to answer the question. But lets dig into the details as much as I can.
DISCLAIMER: I am no expert in boost::mpl.
As per the comment below by the OP, the original code works if we change my_type to:
template <class T>
struct my_type{};
But that doesn't go well with what I mentioned previously i.e operation needs a type identifier. So, lets see, what mpl is doing under the hood:
struct transform somewhat looks like:
template<
typename Seq1 = mpl::na
, typename Seq2OrOperation = mpl::na
, typename OperationOrInserter = mpl::na
, typename Inserter = mpl::na
>
struct transform {
boost::mpl::eval_if<
boost::mpl::or_<
boost::mpl::is_na<OperationOrInserter>,
boost::mpl::is_lambda_expression<my_type<mpl_::arg<1> > >,
boost::mpl::not_<boost::mpl::is_sequence<my_type<mpl_::arg<1> > > >,
mpl_::bool_<false>,
mpl_::bool_<false>
>,
boost::mpl::transform1<
boost::mpl::vector<int, double>,
my_type<mpl_::arg<1>>,
mpl_::na
>,
boost::mpl::transform2<boost::mpl::vector<int, double>,
my_type<mpl_::arg<1> >,
mpl_::na, mpl_::na>
>
};
The important part to look here is the is_lambda_expression metafunction which basically checks if your Operation meets the requirement of a metafunction.
After applying some heavy macro and template machinery and specializations, the above check synthesises below struct:
template<
typename IsLE, typename Tag
, template< typename P1 > class F
, typename L1
>
struct le_result1
{
typedef F<
typename L1::type
> result_;
typedef result_ type;
};
Here, F is your my_type and L1 is the placeholder . So, in essence the above struct is nothing but add_my_type which I had shown in my initial response.
If I am correct till now, le_result1 is the operation that would be performed on your sequence.
(This question has an answer by Nim which mentions boost::mpl::map.)
Is there a compile-time container in standard C++ which can hold types?
A usage example would be:
compiler::vector foo{char, short, long, long long};
template <int N>
void bar(foo[N] param){/*do something and return foo[N]*/}
In c++11 you can use std::tuple : (disclaimer: not tested)
#include <tuple>
#include <type_traits>
std::tuple<char, short, long, long long> foo;
// reference type
template <int N>
void bar(decltype(std::get<N>(foo)) param){...}
// value type
template <int N>
void bar(std::remove_reference<decltype(std::get<N>(foo))>::type param)
Note that this is not completely what you want since you will either have only value or reference types, even if both are mixed in the tuple declaration of foo.
The value of the tuple are never used. I think with compiler optimization, foo will actually never be instanciated in the object code
As a type container, the standard provides you with std::tuple and -- as bogdan commented -- you can access the type elements using std::tuple_element.
using foo = std::tuple<char, short&, const long&&, long long>;
template <int N>
typename std::tuple_element<N,foo>::type bar(){/*...*/}
Even if std::tuple_element did not exist, you could easily build your own:
/// We want a metafunction to accept an index N into our type list LIST
template <unsigned N, typename LIST> struct
tuple_element;
/// Specialization for the case where N==0
template <template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<0,LIST_T<T,ELMS...>> {
using type = T; // just take the first type from the list
};
template <unsigned N, template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<N,LIST_T<T,ELMS...>> {
/// fallback for N>0: delegate the result type recursively until N->0
using type = typename tuple_element<N-1,LIST_T<ELMS...>>::type;
};
// create a convenience wrapper for the template
template <unsigned N, typename LIST> using
type_at = typename tuple_element<N, LIST>::type;
Now you can define your type list, e.g. like so:
using foo = std::tuple<char, short&, const long&&, long long>;
And you can easily access it's elements using type_at<N, foo>:
static_assert(std::is_same< type_at<0,foo>, char>::value,"error");
static_assert(std::is_same< type_at<1,foo>, short&>::value,"error");
static_assert(std::is_same< type_at<2,foo>, const long&&>::value,"error");
static_assert(std::is_same< type_at<3,foo>, long long>::value,"error");
template <int N>
type_at<N,foo> bar(){/*...*/}
This build's on BĂ©renger's answer, which put me onto the tuple concept. But I believe we can do better, even preserving references:
tuple foo<char&, short&, long&, long long&>;
template <int N>
void bar(tuple_element_t<N, decltype(foo)> param){}
In fact if there are no plans to use foo beyond this function we can even declare it inline:
template <int N>
void bar(tuple_element_t<N, tuple<char&, short&, long&, long long&>> param){}
Let's say I have a Filter metafunction that filters a list of types:
template<template<typename> class TFilter, typename... Ts>
using Filter = MetaList</* check TFilter<T>{}() for every type in Ts... */>;
The metafunction can be used like this:
Filter<std::is_pod, int, char, std::string, int>
// ...returns...
MetaList<int, char, int>
Now, I'd like to get all types that are not POD. I could create a FilterNot metafunction, but I actually need the "negation" in other metafunctions as well.
Is it possible to create a negation wrapper for any type-trait-like template class?
Desired code:
Filter<Negate<std::is_pod>, int, char, std::string, int>
// ...returns...
MetaList<std::string>
template<template<class...>class Z>
struct negate {
template<class...Ts>
using result=std::integral_constant<bool, !Z<Ts...>::value>;
};
Filter<negate<std::is_pod>:: template result, int, char, std::string, int>;
or
Filter<typename negate<std::is_pod>::result, int, char, std::string, int>;
depending on compiler should work. (IIRC, some compilers are quirky about this)
As I find this syntax awkward, maybe take a list of tests to daisy chain? Then negation is just another trait on the stack:
template<template<class...>class... Zs>
struct tests {};
then take a tests and apply the Zs recursively.
template<class B>
using negate=std::integral_constant<bool,!B::value>;
filters<tests<negate, std::is_pod>, int, std::string>
Yet another approach is to take your tests and make them constexpr functions on tag<type>{} (easy to do), which are syntactically easier to compose. Going from template<class>class to constexpr bool(tag<class>) isn't hard.
One way to do that:
#include <iostream>
#include <type_traits>
using namespace std;
template<template <class> class P>
struct invert
{
template<class... Args>
struct templ {
static constexpr bool value = ! P<Args...>::value;
};
};
int main()
{
cout << invert<is_pod>::templ<string>::value << endl;
cout << is_pod<string>::value << endl;
return 0;
}