Now that my previous question has a solution, more questions arise.
I want to use the wrap_into_container meta-function with boost::mpl::transform, e.g.:
#include <vector>
#include <list>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/mpl.hpp>
#include <boost/mpl/transform.hpp>
namespace container
{
template <typename T> struct vector { typedef std::vector<T> type; };
template <typename T> struct list { typedef std::list<T> type; };
}
template<typename T, template <typename> class Container>
struct wrap_into_container
{
typedef typename Container<T>::type type;
};
int main()
{
namespace fusion = boost::fusion;
namespace mpl = boost::mpl;
typedef fusion::vector<int, float, int> vec_type;
typedef mpl::transform< vec_type, wrap_into_container<mpl::_1, container::vector> >::type wrapped_vec_type;
wrapped_vec_type w;
return w.size();
}
Link to coliru
But it seems like I cannot pass a template template parameter into mpl::transform ...
How can I solve this? Please provide a C++03 solution, since I cannot use C++11.
In boost::mpl, higher order functions are written by passing a fixed type with an internal apply template member (known as a metafunction class), rather than through the use of template-template parameters. Live Example.
#include <vector>
#include <list>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/mpl.hpp>
#include <boost/mpl/transform.hpp>
#include <iostream>
namespace container
{
struct vector {
template<typename T> struct apply {
typedef std::vector<T> type;
};
};
struct list {
template <typename T> struct apply {
typedef std::list<T> type;
};
};
}
template<typename T, typename ContainerMaker>
struct wrap_into_container
{
typedef typename ContainerMaker::template apply<T>::type type;
};
int main()
{
namespace fusion = boost::fusion;
namespace mpl = boost::mpl;
typedef fusion::vector<int, float, int> vec_type;
typedef mpl::transform<
vec_type,
wrap_into_container<mpl::_1, container::vector>
>::type wrapped_vec_type;
wrapped_vec_type w;
std::cout << size(w) << "\n";
return size(w);
}
I don't know boost::mpl, so I can only speculate from what I've seen in the documentation.
I think what you need is
template<template <typename> class Container>
struct wrap_into_container
{
template<typename T>
struct map
{
typedef typename Container<T>::type type;
};
};
Followed by
typedef wrap_into_container<container::vector>::template map<mpl::_1> fun;
typedef transform<vec_type, fun>::type wrapped_vec_type;
In this case, fun is a class of the form C<mpl::_1> where C is a class template, and whose ::type is std::vector<mpl::_1>. I think this is what mpl::transform expects for its type map.
My only test is with my own version of transform, which works with template template arguments instead of placeholders for the type map. Check live example, where transform is defined using C++11 but the remaining part is C++03. In this example, I am only using
wrap_into_container<container::vector>::template map
as a template template argument to my transform, without the placeholder <mpl::_1>.
I hope this helps.
Related
In my c++ template struct I want to use different container types which use different allocators, e.g. std::vector and thrust::device_vector.
I need to specify the allocator explicitely, otherwise I get "wrong number of template arguments (1, should be 2)":
template<typename T, template <typename, typename> class Container, typename Alloc>
struct wrap_into_container
{
typedef Container<T, Alloc> type;
};
Since the different container classes use different allocators, I have to specify the corresponding allocator everytime I want to use this template.
How can I get the allocator depending on the Container type without having to specify it?
I thought of using a traits struct which I then specialize for each Container type, but I don't know how to implement it or if it is even useful / possible / ...
UPDATE:
I cannot use C++11 unfortunately due to restrictions of the NVIDIA compiler ...
In c++11, I favour variadics
template<typename T, template <typename...> class Container>
struct wrap_into_container
{
typedef Container<T>::type type;
};
(I haven't checked whether C::type is actually a wellformed expression for standard container types)
To the comment:
template<typename T, template <typename...> class Container>
struct wrap_into_container
{
typedef Container<T>::type type;
};
For C++03 you can emulate a template alias using nested typedefs, essentially making a unary type-function taking a single element type and returning a container of that type. The concept:
#include <vector>
#include <deque>
#include <set>
#include <list>
namespace container
{
template <typename T> struct vector { typedef std::vector<T> type; };
template <typename T> struct set { typedef std::set <T> type; };
template <typename T> struct list { typedef std::list <T> type; };
template <typename T> struct deque { typedef std::deque <T> type; };
}
template<typename T, template <typename> class Container>
struct wrap_into_container
{
typedef typename Container<T>::type type;
};
#include <string>
int main() {
wrap_into_container<int, container::set>::type ws;
wrap_into_container<double, container::list>::type wl;
wrap_into_container<bool, container::deque>::type wd;
wrap_into_container<std::string, container::vector>::type wv;
return ws.size() + wl.size() + wd.size() + wv.size();
}
See it Live On Coliru
basically, i'd like to (at compile time) get the twice-as-wide type from a stdint type. I can do it by hand like this
template <typename T>
class twice_as_wide{};
template<>
class twice_as_wide<uint8_t>
{
public:
typedef uint16_t type;
};
template<>
class twice_as_wide<int8_t>
{
public:
typedef int16_t type;
};
template<>
class twice_as_wide<uint16_t>
{
public:
typedef uint32_t type;
};
ect, I just want to make sure that this doesn't exist yet. I'm using visual studio 2010 C++0X (annoying, i know) and already have a boost dependency. Does anyone know of an existing implementation of this?
If you don't mind another boost dependency, then you could do this:
#include <type_traits>
#include <boost/integer.hpp>
template <typename T, bool is_unsigned = std::is_unsigned<T>::value>
struct twice_as_wide
{
typedef typename boost::uint_t< 2 * std::numeric_limits<T>::digits>::exact type;
};
template<typename T>
struct twice_as_wide<T, false>
{
typedef typename boost::int_t< 2 * (std::numeric_limits<T>::digits + 1)>::exact type;
};
template< typename T>
using twice_as_wide_t = typename twice_as_wide<T>::type;
I'd say, use Boost Integer. Demo that keeps signed-ness of the source type: Live on Coliru
#include <boost/integer.hpp>
#include <limits>
namespace helpers
{
// wrappers around Boost Integer http://www.boost.org/doc/libs/1_54_0/libs/integer/doc/html/boost_integer/integer.html#boost_integer.integer.sized
template <bool is_signed, int bin_digits> struct select_twice;
template <int bin_digits> struct select_twice<true, bin_digits> {
using type = typename boost::int_t<bin_digits + 1>::least;
};
template <int bin_digits> struct select_twice<false, bin_digits> {
using type = typename boost::uint_t<bin_digits>::least;
};
}
template <typename Int>
using select_twice = helpers::select_twice<std::numeric_limits<Int>::is_signed, std::numeric_limits<Int>::digits*2>;
template <typename Int>
using twice_t = typename select_twice<Int>::type;
int main()
{
static_assert(std::is_same<uint16_t, twice_t<uint8_t>>::value, "oops");
static_assert(std::is_same<uint32_t, twice_t<uint16_t>>::value, "oops");
static_assert(std::is_same<uint64_t, twice_t<uint32_t>>::value, "oops");
}
So I have the following case, apologies for the long example, but it should compile correctly:
#include <tuple>
#include <functional>
#include <iostream>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/vector.hpp>
namespace mpl = boost::mpl;
namespace aux
{
template <typename ...Args>
struct to_vector
{ };
template <typename T, typename ...Args>
struct to_vector<T, Args...>
{ typedef typename mpl::push_front<typename to_vector<Args...>::type, T>::type type; };
template <typename T>
struct to_vector<T>
{ typedef typename mpl::vector<T> type; };
template <>
struct to_vector<>
{ typedef typename mpl::vector<> type; };
template <typename Dest, typename T>
struct tuple_adder
{
typedef decltype(std::tuple_cat(std::declval<Dest>(), std::make_tuple(std::declval<T>()))) type;
};
}
struct foo
{
struct storage
{ };
template <typename T>
struct placeholder : storage
{
placeholder(T&& t) : content(t)
{ }
T content;
};
storage* data;
template <typename ...Args>
foo(Args&&... args)
: data()
{
typedef typename mpl::fold<
typename aux::to_vector<Args...>::type,
std::tuple<>,
aux::tuple_adder<mpl::_1, mpl::_2>
>::type tuple_type;
// Instantiate the tuple
data = new placeholder<tuple_type>(std::make_tuple(std::forward<Args>(args)...));
}
template <typename ...Args>
void set(Args&&... args)
{
typedef typename mpl::fold<
typename aux::to_vector<Args...>::type,
std::tuple<>,
aux::tuple_adder<mpl::_1, mpl::_2>
>::type tuple_type;
auto tp = static_cast<placeholder<tuple_type>*>(data);
*tp = std::make_tuple(std::forward<Args>(args)...);
}
};
int main()
{
foo f(1, 2., std::string("Hello"));
f.set(4, 3., std::string("Bar"));
f.set(3., std::string("Bar"), 3.);
}
The idea is simple, foo utilises type erasure to store a tuple that is constructed via the constructor. Then the restriction should be that set is only allowable where the tuple generated from the variadic argument list matches the held tuple (which has unfortunately had it's type erased.)
Now I can detect this at runtime using typeid(), however, I'd like to know if there is a way I can have the same detection at compile time. The only constraints are that foo cannot be templated, and variant is out as I want to allow foo to be constructed with the fields as necessary (all specified at compile time...)
I fear the answer is that this is not possible (due to the type erasure), however I'm hoping for some ideas of a way to achieve this functionality...
The point of a compile time type system is that it constrains the allowable operations on values of types. If two objects are the same type then they admit the same allowable operations.
So no, there's no way for the compiler to know what you want to allow, given that you've erased the distinction.
Is there a way to do this with some c++11 or at most a boost library?
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T> class remove_all_pointers{
public:
typedef T type;
};
template <typename T> class remove_all_pointers<T*>{
public:
typedef typename remove_all_pointers<T>::type type;
};
int main(){
//correctly prints 'i' on gcc
cout<<typeid(remove_all_pointers<int****>::type).name()<<endl;
}
That doesn't quite work for all pointer types. You need to account for different cv-qualifiers as well:
template <typename T> class remove_all_pointers<T* const>{
public:
typedef typename remove_all_pointers<T>::type type;
};
template <typename T> class remove_all_pointers<T* volatile>{
public:
typedef typename remove_all_pointers<T>::type type;
};
template <typename T> class remove_all_pointers<T* const volatile >{
public:
typedef typename remove_all_pointers<T>::type type;
};
Since C++17 you can create a readable, simple and cv-qualifier aware meta function.
Use it like:
int main()
{
remove_all_pointers_t<int* const* volatile* const volatile*> v = 42;
return 0;
}
C++20
#include <type_traits>
template<typename T>
struct remove_all_pointers : std::conditional_t<
std::is_pointer_v<T>,
remove_all_pointers<
std::remove_pointer_t<T>
>,
std::type_identity<T>
>
{};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
C++17
In C++17 std::type_identity isn't available yet and std::identity isn't available anymore, hence you need to create your own 'identity' meta function:
#include <type_traits>
// your custom 'identity' meta function
template <typename T>
struct identity
{
using type = T;
};
template<typename T>
struct remove_all_pointers : std::conditional_t<
std::is_pointer_v<T>,
remove_all_pointers<
std::remove_pointer_t<T>
>,
identity<T>
>
{};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
Neither Boost nor C++11 features such a trait template. But your code should work.
I'm looking for a way to map types, f.i. having a class Double:
class Double
{
public:
typedef double basic_type;
...
};
I'd like to be able to have a type caster so that
typeid(TypeToObjectType<double>::type) == typeid(Double)
Any ideas how to accomplish this (through partial specializations etc.) ?
You can achieve this through specialization :
template<class T>
struct TypeToObjectType;
template<>
struct TypeToObjectType<double> {
typedef Double type;
};
Note that you have to provide a specialization for each of the types on which you want TypeToObjectType to work. Macros can be helpful here :
#define SPECIALIZE_TYPETOOBJECTTYPE(ObjectType) \
template<> struct TypeToObjectType<ObjectType::basic_type> { \
typedef ObjectType type; \
};
SPECIALIZE_TYPETOOBJECTTYPE(Int)
SPECIALIZE_TYPETOOBJECTTYPE(Double)
Sounds like you are looking for something like this:
template<typename T>
struct TypeToObjectType;
// specialization for T=double
template<>
struct TypeToObjectType<double> {
typedef Double type;
};
Here TypeToObjectType<double>::type is Double and you can add other specializations for additional mappings.
This should work very well, however, I haven't tested this method for classes of mine. I have used an idea from the book "C++ Templates: The Complete Guide", paragraph 20.4.2
#include <iostream>
#include <string>
#include <list>
#include <type_traits>
template<typename... Ts>
struct type_mapper;
template<>
struct type_mapper<>
{
static void mapTo(...);
};
template<typename T, typename... Ts>
struct type_mapper<T, Ts...> : type_mapper<Ts...>
{
static typename T::second_type mapTo(typename T::first_type);
using type_mapper<Ts...>::mapTo;
};
template<typename T, typename MapperT>
struct GetTypeOnMap
{
using type = decltype(MapperT::mapTo(std::declval<T>()));
};
template<typename T, typename MapperT>
using get_type_on_mapping = typename GetTypeOnMap<T, MapperT>::type;
int main(void)
{
using mapper = type_mapper <
std::pair<int, double>,
std::pair<double, int>,
std::pair<float, std::string>>;
using shouldBeDouble = get_type_on_mapping<int, mapper>;
using shouldBeString = get_type_on_mapping<float, mapper>;
std::cout << shouldBeDouble{2.9};
std::cout << shouldBeString{"Hello"};
return 0;
}
EDIT:
Another one solution. More concise and flexible:
#include <complex>
#include <type_traits>
#include <cstdint>
#include <iostream>
#include <string>
template <typename V1, typename V2, typename T>
struct OnTypesEqual : std::bool_constant<std::is_same_v<V1, V2>> {
using type = T;
};
template <typename T>
struct default_type : std::true_type {
using type = T;
};
template<typename T>
using TypesMapper = typename std::disjunction<
OnTypesEqual<T, double, std::int8_t>,
OnTypesEqual<T, float, std::int16_t>,
OnTypesEqual<T, std::string, std::int32_t>,
OnTypesEqual<T, char, std::int64_t>,
default_type<void>
>::type;
int main()
{
std::cout << typeid(TypesMapper<double>).name() << std::endl;
std::cout << typeid(TypesMapper<float>).name() << std::endl;
std::cout << typeid(TypesMapper<std::string>).name() << std::endl;
std::cout << typeid(TypesMapper<char>).name() << std::endl;
return 0;
}