Boost MPL recursive metafunction definition - c++

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?

Related

C++ Type alias error: expected unqualified-id before 'using'

There are numerous resources explaining how to do Type aliasing, but clearly I am missing something. I am trying to create a map alias. When debugging I want my Map_t to be the standard library map since it is easier to see what it contains, and when not debugging I want it to be btree::btree_map since it uses less memory.
To do this I tried the following:
#if DEBUG
#include <map>
template<typename U, typename V>
using Map_t = map<typename U, typename V>; // <--- Error on this line
#else
#include "btree_map.h"
template<typename U, typename V>
using Map_t = btree::btree_map<typename U,typename V>; // <--- Error on this line
#endif
When compiling I get the error error: expected unqualified-id before 'using'. I've tried several variations of the above with no success. Thanks in advance for your comments.
UPDATE:
I feel I may have made this a little confusing be including the btree portion. SO let me expand a little. If I declare my types as say either
btree::btree_map<int, int> mymap;
or
map<int, int> mymap;
all is fine. But just trying to introduce
template<typename U, typename V>
using Map_t = map<U, V>;
Map_t<int, int> mymap;
leads the the error.
Your syntax is slightly wrong. You need
template<typename U, typename V>
using Map_t = map<U, V>;
As for the btree segment, it depends on the details of btree. If it is a namespace with a class template called btree_map, then you need
template<typename U, typename V>
using Map_t = btree::btree_map<U, V>;
If it is a class template with an inner type, then you may need something like
template<typename U, typename V>
using Map_t = typename btree<U,V>::btree_map;

CRTP-related compiler error on typedef

I have rather simple, I imagine, question about CRTP, but I cannot seem to find an answer to it. Probably, because it is so simple, no one thought of asking it. I am new to the concept, so, please don't laugh too hard ;).
Here is the code (it's sort of an attempt to do something similar to STL containers):
template< typename tT >
struct TBase
{
typedef tT T;
};
template< typename tTBase >
struct TTraitsBase
{
typedef typename tTBase::T T;
};
template< typename tTHelpee, typename tTTraits >
struct THelper
{
typedef typename tTTraits::T T;
typedef typename tTHelpee::T Th; /* This generates a compiler error:
'T' not being a member of TDerived<tT> */
T Foo( void )
{
return static_cast< tTHelpee* > ( this )->mVal;
}
};
template< typename tT >
struct TDerived : TBase< tT > , THelper< TDerived< tT > , TTraitsBase< TBase< tT > > >
{
using TBase< tT >::T;
T mVal;
};
int main()
{
TDerived< int >::T lTmp = -1;
TDerived< int > lObj;
lObj.mVal = -1;
std::cout << lObj.Foo() << std::endl;
return 0;
}
Everything compiles if I comment the offending typedef typename _THelpee::T Th;. And that what confuses me: if compiler does not like typedef typename _THelpee::T Th;, why does it let through static_cast< _THelpee* > ( this )->mVal? I assume, it has something to do with not being able to instantiate THelper when instantiating TDerived, but no clear understanding. Can someone, please, give a brief explanation and/or some references on what is happening here? Thank you.
EDIT: removed '_T' prefix.
Implicitly instantiating a class does not instantiate its member function definitions. Each member of a class template is instantiated only when used (unless you use explicit instantiation).
The first thing you implicitly instantiate is TDerived<int>, on the first line of main. To instantiate that, the compiler looks up the TDerived template, sees that there are a couple of dependent base classes, so tries to instantiate those. TBase<int> instantiates with no problem. But on trying to instantiate THelper< TDerived<int>, TTraitsBase< TBase<int> > >, there's an attempt to get a member TDerived<int>::T, but TDerived<int> is still an incomplete type.
Instantiating THelper< ... > also notes that there is a member function int Foo(), but does not evaluate the semantics of its definition.
By the time you call lObj.Foo() from main, which instantiates that int THelper< ... >::Foo(), TDerived<int> is a complete type, so there's no problem there.
There are couple of problem with the code posted. Since, I couldn't make out the intention, I will list what I found erroneous.
(1) Using an incomplete type:
template< typename _T >
struct TDerived : TBase< _T > , THelper< TDerived< _T > , TTraitsBase< TBase< _T > > >
^^^^^^^^^^^^^^ it's incomplete
IMO when you are defining the body of TDerived<_T>, you should not use the same as a parameter to anything.
(2) Improper type definition.
using TBase< _T >::T;
should be,
typedef typename TBase< _T >::T T;
I'd say you have a circular dependency in your CRTP (as one would have, by its very nature), but that means that the base template class isn't allowed to use any of its parameter class, because that very parameter class isn't defined yet - it's still an incomplete type at the time where Base<T> is instantiated.
So, this is OK:
template <typename T> struct Base
{
T * impl; // incomplete type is OK
};
class Foo : public Base<Foo> { /* ... */ };
But this isn't:
template <typename T> struct Base
{
T x; // need to know T, but T depends on Base<T>!
};

How to typedef a type derived through several layers of templates?

Maybe my Google-fu just isn't strong enough.
Using GCC 4.4.3, I've got a set of classes like this:
template <typename storage_t, typename index_t = std::size_t, typename
leaf_payload_t = std::size_t>
struct btree_node {
public:
typedef btree_node<storage_t, index_t, leaf_payload_t> this_t;
typedef boost::shared_ptr<this_t> handle_t;
// [...]
};
template <typename storage_t, typename index_t = std::size_t, typename
leaf_payload_t = std::size_t>
class btree {
public:
class caching_storage_t;
typedef btree_node<caching_storage_t, index_t, leaf_payload_t> node_t;
typedef typename node_t::handle_t nodehandle_t;
// [...]
class caching_storage_t {
public:
//typedef typename btree::nodehandle_t nodehandle_t; // Fails -- why?
typedef typename boost::shared_ptr<node_t> nodehandle_t;
// [...]
};
};
As you can see, I've had to redefine nodehandle_t in caching_storage_t, because if I try it with the commented-out typedef line (which I'd prefer), I get an error "no type named ‘handle_t’ in ‘struct btree_node<...>’" -- which is obviously incorrect, and the compiler knows it since the typedef works fine in btree. I've also tried a using typename btree::nodehandle_t;, and every variation I've been able to think of on both, to no avail.
Is this a language/syntax problem (and if so, what is the right syntax), or is it a compiler bug?
(There's a similar question here, but it doesn't seem to apply because the thing I'm trying to typedef isn't itself a template. Nothing else I've been able to find seems even close.)
It looks like a compiler bug to me. To be sure, I put your code into clang and instantiated btree<int>::caching_storage_t, all working fine also with the comment chars removed.
It also works on GCC4.5.1 and GCC4.3.4.

Combination of types using boost::mpl

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.

Check if parameter pack contains a type

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 > {};