Get twice as wide type - c++

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");
}

Related

Typetrait to get value type of contiguous-memory containers

I need a typetrait that would return value type for the following "contiguous-memory" types (to which operator[] can be applied):
std::vector<T, Args...>, std::array<T, N>, T[], T[N], and T* (with possibly CV qualifiers like T* const). The trait should return T for all above types.
Is there a more concise implementation in C++17, than the tedious one below? Like somehow gathering all pointer cases in one specialization and appliyng std::remove_ptr, same for T[N] with std::remove_extent.
template<class T> struct remove_array_like;
template<class T> struct remove_array_like<T*> { using type = T; };
template<class T> struct remove_array_like<T* const> { using type = T; };
template<class T> struct remove_array_like<T* volatile> { using type = T; };
template<class T> struct remove_array_like<T* const volatile> { using type = T; };
template<class T> struct remove_array_like<T[]> { using type = T; };
template<class T, std::size_t N>
struct remove_array_like<T[N]> { using type = T; };
template<class T, std::size_t N>
struct remove_array_like<std::array<T, N>> { using type = T; };
template<class T, class... Args>
struct remove_array_like<std::vector<T, Args...>> { using type = T; };
I need a typetrait that would return value type for the following "contiguous-memory" types (to which operator[] can be applied):
To "extract" the required type, it seems to me that you can simply use the operator[] itself, removing references, volatiles and consts. See the following helper struct
template <typename T>
struct contained_type
{ using type = std::remove_cv_t<
std::remove_reference_t<
decltype(std::declval<T>()[0])>>; };
You can write your contained_type struct using SFINAE, so with an additional defaulted template parameter
template <typename, typename = void>
struct remove_array_like;
It seems to me that you needs only three specializations.
One for contigous-memory containers (so for containers with a data() method that return a pointer to the start of the contigous contained memory), that is for std::vector, std::array, std::string and other strings types
template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
: public contained_type<T>
{ };
one for pointers
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
: public contained_type<T>
{ };
and one for arrays
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
: public contained_type<T>
{ };
The following is a full compiling C++17 example
#include <set>
#include <array>
#include <deque>
#include <vector>
#include <string>
#include <type_traits>
template <typename T>
struct contained_type
{ using type = std::remove_cv_t<
std::remove_reference_t<
decltype(std::declval<T>()[0])>>; };
template <typename, typename = void>
struct remove_array_like;
template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
: public contained_type<T>
{ };
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
: public contained_type<T>
{ };
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
: public contained_type<T>
{ };
int main ()
{
using T1 = remove_array_like<std::vector<int>>::type;
//using T2 = remove_array_like<std::deque<int>>::type; // error! no data(), no contigous
using T3 = remove_array_like<std::array<int, 1u>>::type;
using T4 = remove_array_like<std::string>::type;
using T5 = remove_array_like<int * volatile>::type;
using T6 = remove_array_like<int const [1u]>::type;
using T7 = remove_array_like<int volatile []>::type;
// using T8 = remove_array_like<std::set<int>>::type; // error!
// using T9 = remove_array_like<int>::type; // error!
static_assert( std::is_same_v<T1, int> );
static_assert( std::is_same_v<T3, int> );
static_assert( std::is_same_v<T4, char> );
static_assert( std::is_same_v<T5, int> );
static_assert( std::is_same_v<T6, int> );
static_assert( std::is_same_v<T7, int> );
}
Starting from C++20 you can use std::remove_cvref, so contained_type can be simplified as follows
template <typename T>
struct contained_type
{ using type = std::remove_cvref_t<
decltype(std::declval<T>()[0])>; };

Parameter pack used for default template parameter

I'd like to do something like this:
template<int... myints>
struct A
{}
template<int... myints, class traits = A<myints...>>
struct B
{}
I know that parameter packs should appear only at the end, but I remember there are some exceptions. Do you think that is there a way to make this code (or something similar) work?
Thanks
One way:
wrap the types into tuples.
use specialisation to convert type lists to types
#include <tuple>
#include <utility>
template<int...myints> struct A {};
namespace detail {
template<class Sequence>
struct make_A_from;
template<class T, T...ts>
struct make_A_from<std::integer_sequence<T, ts...>> {
using type = A<ts...>;
};
}
template<class Sequence> using make_A_from = typename detail::make_A_from<Sequence>::type;
template<class Sequence, class TraitsTuple = make_A_from<Sequence> >
struct B;
template<typename Int, Int...myints, class Traits>
struct B<std::integer_sequence<Int, myints...>, Traits>
{
using ints_tuple = std::tuple<std::integral_constant<Int, myints>...>;
using traits_type = Traits;
};
namespace detail {
template<typename Int, Int...is>
struct make_B
{
using type = B<std::integer_sequence<Int, is...>>;
};
}
template<int...is> using make_B = B<std::integer_sequence<int, is...>>;
int main()
{
make_B<1,3,4,5> b;
B<std::integer_sequence<int, 5,6,7,8>> b2;
B<std::integer_sequence<int, 5,6,7,8>, class CustomTrait> b3;
}

c++ template: boost::mpl::transform with template template parameter

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.

Is it possible to detect the following assignment at compile time?

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.

standard c++11 way to remove all pointers of a type

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.