I have a list of types, from which I want to construct the list of all combinations with two elements. For example:
namespace mpl = boost::mpl;
typedef mpl::vector<int, long> typelist;
// mpl magic...
// the wanted list is equivalent to:
typedef mpl::vector<pair<int, int>, pair<int, long>,
pair<long, int>, pair<long, long> > combinations;
Here, pair<T1,T2> could be std::pair<T1,T2>, or mpl::vector<T1,T2>.
How to do this?
I would also be interested in removing the duplicates when we consider that pair<T1, T2> == pair<T2, T1>.
Thanks.
The list of combinations of a single type int with the list of types mpl::vector<int, long> can be computed by invoking mpl::fold:
typedef fold<
mpl::vector<int, long>, vector<>,
push_back<mpl::_1, std::pair<int, mpl::_2> >
>::type list_of_pairs;
Now, if we wrap that into a separate meta-function and invoke it for all types of the initial typelist we get:
typedef mpl::vector<int, long> typelist;
template <typename T, typename Result>
struct list_of_pairs
: mpl::fold<typelist, Result,
mpl::push_back<mpl::_1, std::pair<T, mpl::_2> > >
{};
typedef mpl::fold<
typelist, mpl::vector<>, mpl::lambda<list_of_pairs<mpl::_2, mpl::_1> >
>::type result_type;
BOOST_MPL_ASSERT(
mpl::equal<result_type,
mpl::vector4<
std::pair<int, int>, std::pair<int,long>,
std::pair<long,int>, std::pair<long,long>
> >::value);
EDIT: answering second question:
Making the result containing only unique elements (in the sense you mentioned) is a bit more involved. First you need to define a meta function comparing two elements and returning mpl::true_/mpl::false_:
template <typename P1, typename P2>
struct pairs_are_equal
: mpl::or_<
mpl::and_<
is_same<typename P1::first_type, typename P2::first_type>,
is_same<typename P1::second_type, typename P2::second_type> >,
mpl::and_<
is_same<typename P1::first_type, typename P2::second_type>,
is_same<typename P1::second_type, typename P2::first_type> > >
{};
Then we need to define a meta-function which tries to find a given element in a given list:
template <typename List, typename T>
struct list_doesnt_have_element
: is_same<
typename mpl::find_if<List, pairs_are_equal<mpl::_1, T> >::type,
typename mpl::end<List>::type>
{};
Now, this can be utilized to build a new list, making sure no duplicates are inserted:
typedef mpl::fold<
result_type, mpl::vector<>,
mpl::if_<
mpl::lambda<list_doesnt_have_element<mpl::_1, mpl::_2> >,
mpl::push_back<mpl::_1, mpl::_2>, mpl::_1>
>::type unique_result_type;
All this is from the top of my head, so it may need some tweaking here or there. But the idea should be correct.
EDIT: minor corrections as outlined by #rafak
Excellent question. There are many interesting ways to solve this. Here is one.
All the unqualified names are in the mpl namespace, except for _1 and _2 which are in mpl::placeholders and boost::is_same, which is found in the type_traits library. The first template is a helper class to generate a list of all pairs consisting of a single element and each element of the given sequence. The second template aggregates all the results together to form the final sequence. Note that the results are not in a vector. You can do that easily with mpl::copy.
template <class Elem, class Seq>
struct single_combo {
typedef typename transform<Seq
,lambda< std::pair<Elem, _1> >
>::type type;
};
template <class Seq>
struct combo {
typedef typename unique<Seq, is_same<_1,_2> >::type U;
typedef typename fold<
typename transform<U
,lambda< single_combo<_1, U> >
>::type
,empty_sequence
,lambda< joint_view<_1,_2> >
>::type type;
};
typedef typename combo<typelist>::type combinations;
Side note: If you're reading this and want a challenge, try answering this question yourself. It's a great plunge into MPL.
I've been doing some metaprogramming myself lately, have you looked into boost::mpl::set? That will eliminate duplicates. As for combinations, that sounds to me like mapping, what about boost::mpl::map? Beware that there are library limits that are imposed on the limits of types the sequences can take, though this can be adjusted with a macro, you're still at the mercy of your compiler's upper limit, depending on the number of types you need to handle.
Related
I’ve been trying to upgrade (and slightly adapt) this solution from 2012 using modern features of C++.
My goal is actually slightly simpler than in that question; I’d like this:
triangularize_t<T0, T1, ..., TN-1, TN>
To be equivalent to this:
std::tuple<
std::tuple<>,
std::tuple<T0>,
std::tuple<T0, T1>,
...
std::tuple<T0, T1, ..., TN-1>
>
Such that std::tuple_element_t<N, result> has N elements. The type TN should not appear anywhere in the output.
Here’s what I’ve worked up to so far:
template <class _Tuple, class _Seq>
struct _tuple_head;
template <class _Tuple, size_t... _N>
struct _tuple_head<_Tuple, std::index_sequence<_N...>> {
using type = std::tuple<std::tuple_element_t<_N, _Tuple>...>;
};
template <class _Tuple, class _Seq>
struct _triangularize_impl;
template <class _Tuple, size_t... _N>
struct _triangularize_impl<_Tuple, std::index_sequence<_N...>> {
using type = std::tuple<typename _tuple_head<_Tuple, std::make_index_sequence<_N>>::type...>;
};
template <class... _Pack>
struct triangularize {
using type = _triangularize_impl<std::tuple<_Pack...>, std::index_sequence_for<_Pack...>>;
};
template <class... _Pack>
using triangularize_t = typename triangularize<_Pack...>::type;
However, it doesn’t quite seem to be expanding the way I’d expect it to. Using triangularize_t<int, float> as a test, error messages seem to report that the output for std::get<0> and 1 return these types (identical for some reason).
_triangularize_impl<tuple<std::__1::tuple<int, float> >, __make_integer_seq<integer_sequence, unsigned long, 1UL> >
_triangularize_impl<tuple<std::__1::tuple<int, float> >, __make_integer_seq<integer_sequence, unsigned long, 1UL> >
What have I missed here?
Maybe someone can make it in a simpler way... but what about as follows?
template <typename T, std::size_t ... Is>
auto gtt_helper (std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, T>...>;
template <typename ... Ts, std::size_t ... Is>
auto getTriTuple (std::index_sequence<Is...>)
-> std::tuple<decltype(gtt_helper<std::tuple<Ts...>>
(std::make_index_sequence<Is>{}))...>;
template <typename ... Ts>
using triTuple
= decltype(getTriTuple<Ts...>(std::index_sequence_for<Ts...>{}));
The following is a full compiling C++14 example
#include <type_traits>
#include <utility>
#include <tuple>
template <typename T, std::size_t ... Is>
auto gtt_helper (std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, T>...>;
template <typename ... Ts, std::size_t ... Is>
auto getTriTuple (std::index_sequence<Is...>)
-> std::tuple<decltype(gtt_helper<std::tuple<Ts...>>
(std::make_index_sequence<Is>{}))...>;
template <typename ... Ts>
using triTuple
= decltype(getTriTuple<Ts...>(std::index_sequence_for<Ts...>{}));
int main ()
{
using T0 = triTuple<char, int, long, long long>;
using T1 = std::tuple<std::tuple<>,
std::tuple<char>,
std::tuple<char, int>,
std::tuple<char, int, long>>;
static_assert( std::is_same<T0, T1>::value, "!" );
}
To respond to your question ("What have I missed here?"), you have missed a typename and a ::type in triangularize
It seems to me that the right version should be
template <class... _Pack>
struct triangularize {
// ..........VVVVVVVV add typename
using type = typename _triangularize_impl<std::tuple<_Pack...>,
std::index_sequence_for<_Pack...>>::type ;
// and add ::type ..........................................................^^^^^^
};
Unfortunately, your (corrected) code seems to works with clang++ but not with g++; I suspect a g++ bug but I'm not sure.
With Boost.Mp11 this is... unfortunately not a one-liner. It takes a couple lines instead.
We define one function to perform a single action: given a list of everything and the next element, append that one (that is, this takes us from the Nth solution to the N+1st solution):
template <typename L, typename T>
using add_one = mp_push_back<L, mp_push_back<mp_back<L>, T>>;
And now fold over that - which just applies that binary function for each argument in turn:
template <typename... Ts>
using triangularize_t = mp_fold<mp_list<Ts...>, tuple<tuple<>>, add_one>;
And check that it's correct:
static_assert(std::is_same_v<triangularize_t<>,
tuple<tuple<>>>);
static_assert(std::is_same_v<triangularize_t<int>,
tuple<tuple<>, tuple<int>>>);
static_assert(std::is_same_v<triangularize_t<int, char>,
tuple<tuple<>, tuple<int>, tuple<int, char>>>);
We can generalize this to work on any class template instead of solely tuple by changing triangularize to use an input list and deduce its initial value from the input argument:
template <typename L>
using triangularize_t = mp_fold<L, mp_push_back<mp_clear<L>, mp_clear<L>>, add_one>;
Which also allows:
static_assert(std::is_same_v<triangularize_t<mp_list<int, char>>,
mp_list<mp_list<>, mp_list<int>, mp_list<int, char>>>);
Or whatever other lists you might want to use (notably not variant, since variant<> is ill-formed).
With Boost.Mp11 this is a one-liner. I just didn't try hard enough last time. Also this solution matches OP's exact specification:
template <typename... Ts>
using triangularize_t =
mp_transform_q<
mp_bind_front<mp_take, std::tuple<Ts...>>,
mp_rename<mp_iota_c<sizeof...(Ts)>, std::tuple>
>;
Lemme explain what this does, assuming Ts... is <int, char>.
mp_iota_c<sizeof...(Ts)> gives the sequence mp_list<mp_int<0>, mp_int<1>>.
mp_rename swaps out one "list" type for another, in this case mp_list for std::tuple so you get std::tuple<mp_int<0>, mp_int<1>>.
mp_bind_front<mp_take, std::tuple<Ts...>> creates a metafunction on the fly that will take an argument and apply it to mp_take on the full tuple<Ts...>. mp_take takes the first N things from the given list. If we passed in mp_int<1> to this, on our initial tuple<int, char>, we'd get tuple<int>.
mp_transform_q calls the provided metafunction on each element in the list. We take our tuple<mp_int<0>, mp_int<1>> and expand it out into tuple<mp_take<tuple<int, char>, mp_int<0>>, mp_take<tuple<int, char>, mp_int<1>>> which is tuple<tuple<>, tuple<int>>. As desired.
To change this into my other answer (which triangularizes <int> into tuple<tuple<>, tuple<int>>), we can change sizeof...(Ts) into sizeof...(Ts)+1.
To extend this to support any list type (not just tuple), we can change the metafunction here to take a list instead of a pack and use the provided list type as a solution. In some respects, this makes the solution easier:
template <typename L>
using triangularize_t =
mp_transform_q<
mp_bind_front<mp_take, L>,
mp_append<mp_clear<L>, mp_iota<mp_size<L>>>
>;
template <typename... Ts>
using triangularize_t = triangularize_list<std::tuple<Ts...>>;
The awkward part here is the mp_append<mp_clear<L>, mp_iota<mp_size<L>>>. Basically, we need the sequence list to have the same list type as the original list. Before, we could use mp_rename because we know we needed a tuple. But now, we don't have the list as a class template - just have an instance of it. There might be a better way to do this than mp_append<mp_clear<L>, U>... but this is what I have so far.
We can avoid having to use any index_sequences by utilizing partial specialization with multiple parameter packs.
#include <tuple>
template <typename Tuple, typename LastTuple, typename First, typename... Rest>
struct triangulate;
template <typename Tuple, typename LastTuple, typename Last>
struct triangulate<Tuple, LastTuple, Last> {
using type = Tuple;
};
template <typename First, typename Second, typename... TupleTypes, typename... LastTupleTypes, typename... Rest>
struct triangulate<std::tuple<TupleTypes...>, std::tuple<LastTupleTypes...>, First, Second, Rest...> {
using next = std::tuple<LastTupleTypes..., First>;
using type = typename triangulate<std::tuple<TupleTypes..., next>, next, Second, Rest...>::type;
};
template <typename... T>
using triangularize_t = typename triangulate<std::tuple<std::tuple<>>, std::tuple<>, T...>::type;
Passing the end product as the first parameter (std::tuple<std::tuples...>>), and the second parameter is the last std::tuple<...> we used.
We then recursively add the next parameter to the last tuple, and add that tuple to the end of the end-result.
I would be happy to get and advice how to deal with boost::variant in "two dimensional manner". Sounds strange but let my code say more (hopefully):
I have coded a class called Parameter:
template<typename PARAM_TYPE, typename DATA_TYPE=double>
class Parameter : public quantity<PARAM_TYPE, DATA_TYPE>
{
...
}
Example usage of my Parameter as defined above:
Parameter<si::length, double> SampleParameter1;
Parameter<si::dimensionless, short> SampleParameter2;
As I tried to explain by example above, I can define several Parameter types using the boost::units::si::??? and different data type like double, short, int etc.
My GOAL is to construct a std::map container which can store instances of any Parameter type (as sampled above).
Therefore I have declared:
typedef boost::variant<Parameter<si::dimensionless, short>, Parameter<si::length, double> > SupportedParameterTypes;
std::map<int, SupportedParameterTypes> myMapStorage;
This works pretty well but have one big disadvantage I would like to solve - I have to define every single combination of Parameter type I would like to support in SupportedParameterTypes type as defined above.
My idea was to define boost::mpl::vector constaining all the Parameter types I would like to support:
typedef boost::mpl::vector<si::dimensionless, si::length> ParameterTypes;
And all the possible Parameter data types being supported on the other hand:
typedef boost::mpl::vector<short, int, float, double> ParameterDataTypes;
There comes my troubles:
typedef typename boost::make_variant_over<ParameterTypes>::type ParameterTypeVariants;
typedef typename boost::make_variant_over<ParameterDataTypes>::type ParameterDataVariants;
typedef boost::variant<Parameter<ParameterTypeVariants, ParameterDataVariants> > SupportedParameterTypes;
But to define boost::variant of something (Parameter) which is defineed by some other boost::variant seems not to work :o(
QUESTION: How to define std::map container holding all my Parameter types defined in appropriate boost::mpl::vectors?
I would like to kindly ask you for help solving this issue. Maybe it is not good idea/principle at all to code it as I wrote, who knows. My goal is to have a flexible storage by std::map to be capable to hold all my Parameters without having my code ambiguous. Looking for smart solution of course :o)
Many thanks in advance for any reply to my question / request for help
You may produce all your pairing with something like
template <typename Seq, typename T1, typename T2>
struct cartesian_parameters_helper;
template <std::size_t...Is, typename T1, typename T2>
struct cartesian_parameters_helper<std::index_sequence<Is...>, T1, T2>
{
static constexpr std::size_t size1 = std::tuple_size<T1>::value;
using type = boost::variant<
Parameter<
std::tuple_element_t<Is / size1, T1>,
std::tuple_element_t<Is % size1, T2>
>...>;
};
template <typename T1, typename T2>
struct cartesian_parameters
{
using type = typename cartesian_parameters_helper<
std::make_index_sequence<std::tuple_size<T1>::value
* std::tuple_size<T2>::value>,
T1, T2>::type;
};
And then use it as
using SupportedParameterTypes =
cartesian_parameters<std::tuple<si::dimensionless, si::length>,
std::tuple<short, int, float, double>>::type;
Demo
I'm a beginner with MPL so I may well be completely misunderstanding how to use it, but I've tried all I can think of to get this to work.
First, what I'm trying to achieve. I have a bunch of classes of the form:
class X
{
typedef mpl::vector< D1,...,Dn > dependencies;
...
}
where n can be 0 (no dependencies), and the Di are also classes of the same form. My goal is to define a metafunction
template < typename T >
struct unroll_dependencies
{
typedef ??? type;
}
such that 'type' will be an MPL sequence containing T and all of its (recursive) dependencies, with no duplicates and ensuring that any class always comes after its dependencies in the sequence.
My attempt so far is the following (so far I'm ignoring the duplicates issue):
template < typename T, typename Enable = void >
struct unroll_dependencies
{
typedef T t;
typedef typename t::dependencies t_dep;
// For each dependency class Di of T, recursively unroll its dependencies
typedef typename mpl::fold<
t_dep,
mpl::vector0<>,
mpl::lambda<
mpl::copy<
typename unroll_dependencies< _2 >::type, // <-- Compiler error here
mpl::back_inserter< _1 >
>
>
>::type dep_types;
// Finally, add T itself after its dependencies
typedef typename mpl::push_back< dep_types, t >::type type;
};
// Termination case for when a class has no dependencies
template < typename T >
struct unique_dependencies< T, typename boost::enable_if< mpl::empty< typename T::dependencies > >::type >
{
// A sequence containing T alone
typedef mpl::vector1< T > type;
};
Using VC2012, I get a compiler error on the marked line:
error C2146: syntax error : missing ',' before identifier 'type'
I don't really understand well how lambdas and placeholders work, but I'm guessing it's something to do with the combination of this with the recursive definition that is causing the problem. Can anyone explain the reason I'm getting this error? Am I going about the solution entirely the wrong way?
How do I add the numbers?
typedef boost::mpl::vector<
boost::mpl::int_<1>, boost::mpl::int_<2>,
boost::mpl::int_<3>, boost::mpl::int_<4>,
boost::mpl::int_<5>, boost::mpl::int_<6> > ints;
typedef boost::mpl::accumulate<ints, boost::mpl::int_<0>, ????? >::type sum;
EDIT: I was wrong, you can use mpl::plus directly, using the placeholder expressions. This simplifies the whole notation:
typedef mpl::accumulate<ints, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type sum;
Of course it is also possible to obtain the same effect using a metafunction class (which for adding is an overkill, but for something more complex might be reasonable):
struct plus_mpl
{
template <class T1, class T2>
struct apply
{
typedef typename mpl::plus<T1,T2>::type type;
};
};
typedef mpl::accumulate<ints, mpl::int_<0>, plus_mpl >::type sum;
I was wondering if C++0x provides any built-in capabilities to check if a parameter pack of a variadic template contains a specific type. Today, boost:::mpl::contains can be used to accomplish this if you are using boost::mpl::vector as a substitute for variadic templates proper. However, it has serious compilation-time overhead. I suppose, C++0x has compiler-level support for std::is_same. So I was thinking if a generalization like below is also supported in the compiler.
template <typename... Args, typename What>
struct is_present
{
enum { value = (What in Args...)? 1 : 0 };
};
Fortunately, the C++ standard has evolved. With C++1z aka C++17, you can finally iterate easily over parameter packs. So the code for the answer is (almost) as simple, as suggested in the question:
template<typename What, typename ... Args>
struct is_present {
static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};
The weird-looking (std::is_same_v<What, Args> || ...) is expanded by the compiler internally to (std::is_same_v<What, Args[0]> || std::is_same_v<What, Args[1]> || ...), which is exactly, what you want. It even correctly yields false with an empty Args parameter pack.
It is even possible to do the whole check inline in a function or method - no helper structs are required anymore:
template<typename T, typename ... List>
void foo(T t, List ... lst)
{
if constexpr((std::is_same_v<T, List> || ...)) {
std::cout << "T is in List" << std::endl;
} else {
std::cout << "T is not in List" << std::endl;
}
}
Note: This has been taken from another question, that was marked as a duplicate of this question. As this is the "canonical" question for this topic, I added that important information here.
No, you have to use (partial) specialization with variadic templates to do compile-time computations like this:
#include <type_traits>
template < typename Tp, typename... List >
struct contains : std::true_type {};
template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
std::true_type,
contains<Tp, Rest...>
>::type {};
template < typename Tp >
struct contains<Tp> : std::false_type {};
There is only one other intrinsic operation for variadic templates and that is the special form of the sizeof operator which computes the length of the parameter list e.g.:
template < typename... Types >
struct typelist_len
{
const static size_t value = sizeof...(Types);
};
Where are you getting "it has serious compilation-time overhead" with boost mpl from? I hope you are not just making assumptions here. Boost mpl uses techniques such as lazy template instantiation to try and reduce compile-times instead of exploding like naive template meta-programming does.
If you want to avoid manual type recursion, std::common_type appears to me to be the only utility in the STL which is a variadic template, and hence the only one which could potentially encapsulate recursion.
Solution 1
std::common_type finds the least-derived type in a set of types. If we identify numbers with types, specifically high numbers with less-derived types, it finds the greatest number in a set. Then, we have to map equality to the key type onto a level of derivation.
using namespace std;
struct base_one { enum { value = 1 }; };
struct derived_zero : base_one { enum { value = 0 }; };
template< typename A, typename B >
struct type_equal {
typedef derived_zero type;
};
template< typename A >
struct type_equal< A, A > {
typedef base_one type;
};
template< typename Key, typename ... Types >
struct pack_any {
enum { value =
common_type< typename type_equal< Key, Types >::type ... >::type::value };
};
Solution 2
We can hack common_type a little more. The standard says
A program may specialize this trait if
at least one template parameter in the
specialization is a user-defined type.
and describes exactly what is inside it: a recursive partial specialization case, a case which applies a binary operator, and a terminal case. Essentially, it's a generic fold function, and you can add whatever binary operation you please. Here I used addition because it's more informative than OR. Note that is_same returns an integral_constant.
template< typename Addend >
struct type_sum { // need to define a dummy type to turn common_type into a sum
typedef Addend type;
};
namespace std { // allowed to specialize this particular template
template< typename LHS, typename RHS >
struct common_type< type_sum< LHS >, type_sum< RHS > > {
typedef type_sum< integral_constant< int,
LHS::type::value + RHS::type::value > > type; // <= addition here
};
}
template< typename Key, typename ... Types >
struct pack_count : integral_constant< int,
common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};