How has std::rank been implemented? - c++11

I had a look at this archive post
Implementing std::rank for other containers
But it is still unclear to me how the logic of std::rank implementation works.
When I refer to the following
std::rank
I see the following under Possible Implementation
template<class T>
struct rank : public std::integral_constant<std::size_t, 0> {};
template<class T>
struct rank<T[]> : public std::integral_constant<std::size_t, rank<T>::value + 1> {};
template<class T, std::size_t N>
struct rank<T[N]> : public std::integral_constant<std::size_t, rank<T>::value + 1> {};
Could anyone provide a simple explanation about how the above implementation logic works? (does it use variadic templates at all?)

I'll try to explain in a simple way.
With std::rank you want to know the number of dimensions of an array type where a not-array is considered an array of zero dimensions.
Given a type T, you have three cases:
T isn't an array
T is an "array of unknown bound" (see in this page) of elements of another type U, that is an array of unknown size, and we can express is as U[], where U is the other type
T is an array on known size N of elements of type U and we can express it as U[N], where U is the other type.
In cases 2 and 3 the sub-type U can be an array or not, so the game restart.
Look at the "possible implementation" that I have rewritten using U instead of T in cases 2 and 3
// case 1: T isn't an array
template<class T>
struct rank
: public std::integral_constant<std::size_t, 0>
{ };
// case 2: T is an array of unknown bound (U[])
template<class U>
struct rank<U[]>
: public std::integral_constant<std::size_t, rank<U>::value + 1>
{ };
// case 3: T is an array on size N (U[N])
template<class U, std::size_t N>
struct rank<U[N]>
: public std::integral_constant<std::size_t, rank<U>::value + 1>
{ };
When you ask for std::rank<T>::value when a not-array type T, the only version of rank that matches is the first one, so the value is zero (inherited from std::integral_constant<std::size_t, 0>).
Instead, when you ask for std::rank<T>::value when T is an array type (suppose T is int[3], so U is int and N is 3) there are two version of rank that match std::rank<T>: the first one (the main, not specialized) and one of the two partial specializations). But the rules of C++ impose the compiler to choose the most specialized version, so is chose the specialized version (the third version, in case of int[3]) that add 1 at std::rank<U>::value.
A practical example
std::rank<int[1][2][3]>::value
== 1 + std::rank<int[1][2]>::value
== 1 + 1 + std::rank<int[1]>::value
== 1 + 1 + 1 + std::rank<int>::value
== 1 + 1 + 1 + 0
== 3

Related

How to remove the nth element from a tuple?

I'm trying to write a function that creates a new std::tuple from an existing one, with skipping the element on a given index. In example:
I have a tuple t defined as below:
constexpr auto t = std::tuple(1, 2, 3, 4);
And I want to copy it to another tuple. However, I want to skip the nth element. Let's say that in this case, the nth element I want to skip is 3 (this would mean that I want to skip the element with the index 2). This would result in a new tuple defined as:
std::tuple(1, 2, 4);
This is the closest I got until now:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is != N ? std::get<is>(tp) : 0) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof...(elems)>());
}
However, instead of removing the nth element, I set it to 0.
Ideally, I would change the return argument in the function fun() to create a new tuple using multiple temporary tuples:
return std::tuple_cat((is != N ? std::tuple(std::get<is>(tp)) : std::tuple()) ...);
However, the issue with this is that the ternary operator has to have matching types on both sides.
Another approach I tried was based on recursion:
template<std::size_t N, std::size_t head, std::size_t... tail>
constexpr auto fun3() noexcept {
if constexpr(!sizeof...(tail))
return std::tuple(head);
if constexpr(sizeof...(tail) - 1 == N)
return std::tuple_cat(fun3<N, tail...>());
if constexpr(sizeof...(tail) - 1 != N)
return std::tuple_cat(std::tuple(head), fun3<N, tail...>());
}
However, that was even more unsuccessful. In this case, if N is equal to 0, the nth element (which is the first element here as well) will still be used in the new tuple. Also, this won't even compile, because there's an issue with the second statement:
if constexpr(sizeof...(tail) - 1 == N)
What am I missing here? How can I copy a tuple and skip one of its elements during the copy?
I'm using C++17, and I need the function to be evaluated during compile-time.
What about
return std::tuple_cat( foo<is, N>::func(std::get<is>(tp)) ...);
where foo is a struct with specialization as follows?
template <std::size_t, std::size_t>
struct foo
{
template <typename T>
static auto func (T const & t)
{ return std::make_tuple(t); }
}
template <std::size_t N>
struct foo<N, N>
{
template <typename T>
static std::tuple<> func (T const &)
{ return {}; }
}
(caution: code not tested).
This is almost your ternary operator idea but without the problem of matching the types in both sides: only the right type is instantiated.
Another solution would be to create two index sequences that refer to the before and after parts of the tuple.
template<std::size_t nth, std::size_t... Head, std::size_t... Tail, typename... Types>
constexpr auto remove_nth_element_impl(std::index_sequence<Head...>, std::index_sequence<Tail...>, std::tuple<Types...> const& tup) {
return std::tuple{
std::get<Head>(tup)...,
// We +1 to refer one element after the one removed
std::get<Tail + nth + 1>(tup)...
};
}
template<std::size_t nth, typename... Types>
constexpr auto remove_nth_element(std::tuple<Types...> const& tup) {
return remove_nth_element_impl<nth>(
std::make_index_sequence<nth>(), // We -1 to drop one element
std::make_index_sequence<sizeof...(Types) - nth - 1>(),
tup
);
}
Here's a test for this function:
int main() {
constexpr auto tup = std::tuple{1, 1.2, 'c'};
constexpr auto tup2 = remove_nth_element<0>(tup);
constexpr auto tup3 = remove_nth_element<2>(tup);
static_assert(std::is_same_v<decltype(tup2), const std::tuple<double, char>>);
static_assert(std::is_same_v<decltype(tup3), const std::tuple<int, double>>);
return 0;
}
Live example
This solution has the advantages of not constructing intermediary tuples and not using std::tuple_cat, which both can be hard on compile times.
Just a few minutes after posting the question, I found a workaround. It's not ideal, but hey:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is < N ? std::get<is>(tp) : std::get<is+1>(tp)) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof... (elems) - 1>());
}
This way, we copy all of the elements prior to the nth element, and when we reach the nth element, we increase every next index for 1. We won't go out of range, since we pass the index_sequence that has 1 element less than the tuple that is passed.
I hope that this answer helps someone.

Create function with unknown number of std::array [duplicate]

This question already has answers here:
Concatenating a sequence of std::arrays
(9 answers)
Closed 3 years ago.
I am trying to make a function that pass that test:
TEST(MyCat, CheckOn2Arrays){
std::array<float, 3> vec1{1.0f, 2.0f, 3.0f};
std::array<float, 3> vec2{4.0f, 5.0f, 6.0f};
std::array<float, 6> r = MyCat(vec1, vec2);
EXPECT_EQ(r[0], 1.0f);
EXPECT_EQ(r[1], 2.0f);
EXPECT_EQ(r[2], 3.0f);
EXPECT_EQ(r[3], 4.0f);
EXPECT_EQ(r[4], 5.0f);
EXPECT_EQ(r[5], 6.0f);
}
I have writen that function:
template<class T, size_t N>
auto MyCat((std::array<T, N>) ... arrays) ->
decltype(std::array<T, N*sizeof...(arrays)>) {
std::array<T, sizeof...(arrays)*N> retArray;
int i = 0;
for(std::array<T, N> array: arrays){
T* = array.begin();
while(!T){
retArray[i] = &T;
i++;
T++;
}
}
return retArray;
}
The function must take an arbitrary number of arguments and return an object of type std::array. All arguments are of type std::array (T and N are the same for all function arguments).
Im getting that errors and cant understand how to solve them..
../src/test/../myproject/MyCat.h:4:29: error: expected primary-expression before ‘)’ token
auto MyCat((std::array<T, N>) ... arrays) ->
^
../src/test/../myproject/MyCat.h:4:6: warning: variable templates only available with
std=c++14 or -std=gnu++14
auto MyCat((std::array<T, N>) ... arrays) ->
^
../src/test/../myproject/MyCat.h:4:43: error: expected ‘;’ before ‘->’ token
auto MyCat((std::array<T, N>) ... arrays) ->
^
../src/test/MyCat_test.cc: In member function ‘virtual void
MyCat_CheckOn2Arrays_Test::TestBody()’:
../src/test/MyCat_test.cc:10:35: error: missing template arguments before ‘(’ token
std::array<float, 6> r = MyCat(vec1, vec2);
Here is a C++17 way to do it, but I don't consider it pretty:
template<class T, std::size_t ... N>
auto MyCat(std::array<T, N> ... arrays)
{
constexpr std::size_t NSum = (N + ...);
std::array<T, NSum> retArray;
auto outIt = retArray.begin();
((outIt = std::copy(arrays.begin(), arrays.end(), outIt)), ...);
return retArray;
}
https://godbolt.org/z/ha9NSY
This function takes any number of std::arrays with the same type but potentially different sizes. (You could disallow different sizes if you wanted, left as exercise to the reader.)
We compute the total size (by summing all N in a fold expression) at compile-time (constexpr) and then create a return array of that size.
Next, the ugly part: We want to perform some operation for every variadic argument, so we use a fold expression on the comma operator. We want to copy the current variadic argument (arrays) to the correct section of retArray. Thankfully, std::copy returns an updated iterator for the output, so we can use that to keep track of where to copy to. Note that you need parenthesis around both the comma-operated expression (the iterator assignment) and around the whole fold expression.
It works, but again, not pretty. And it's not even constexpr (because std::copy is not constexpr)...

derivation of return type based on max range of input possible in C++

I was recently asked this question in an interview of C++ where I
was asked to improve the below piece of code which fails when
adding two int's results in the result being long and return
type needs accordingly to be derived.
Here the below code fails because the decltype() based derivation is not intelligent enough to identify based on the actual range of values of input but the type and derives return type as same. Hence we need perhaps some metaprogramming template technique to derive the return type as long if T is int.
How can this be generalized any hints or clues?
I feel that decltype() won't be helpful here.
#include<iostream>
#include<string>
#include<climits>
using namespace std;
template<typename T> auto adder(const T& i1, const T& i2) -> decltype(i1+i2)
{
return(i1+i2);
}
int main(int argc, char* argv[])
{
cout << adder(INT_MAX-10, INT_MAX-3) << endl; // wrong.
cout << adder<long>(INT_MAX-10, INT_MAX-3) << endl; // correct!!.
return(0);
}
Hence we need perhaps some metaprogramming template technique to derive the return type as long if T is int.
Not so simple.
If T is int, you're non sure that long is enough.
The standard say only that
1) the number of bits for int (sizeof(int) * CHAR_BIT) is at least 16
2) the number of bits for long (sizeof(long) * CHAR_BIT) is at least 32
3) sizeof(int) <= sizeof(long)
So if a compiler manage a int with sizeof(int) == sizeof(long), this is perfectly legal and
adder<long>(INT_MAX-10, INT_MAX-3);
doesn't works because long can be not enough to contain (without overflow) the sum between two int's.
I don't see a simple and elegant solution.
The best that come in my mind is based on the fact that C++11 introduced the following types
1) std::int_least8_t, smallest integer type with at least 8 bits
2) std::int_least16_t, smallest integer type with at least 16 bits
3) std::int_least32_t, smallest integer type with at least 32 bits
4) std::int_least64_t, smallest integer type with at least 64 bits
C++11 also introduce std::intmax_t as the maximum width integer type.
So I propose the following template type selector
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
that, given a number of bits, define the corresponding smallest "at least" integer type.
I propose also the following using
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
that, given a type T, detect the smallest integer type that surely contain a sum between two T values (a integer with a number of bits that is at least the number of bits of T plus one).
So your adder() simply become
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
Observe that th returned value isn't simply
return i1 + i2;
otherwise you return the correct type but with the wrong value: i1 + i2 is calculated as a T value so you can have overflow, then the sum is assigned to a typeNext<T> variable.
To avoid this problem, you have to initialize a typeNext<T> temporary variable with one of two values (typeNext<T>{i1}), then add the other (typeNext<T>{i1} + i2) obtaining a typeNext<T> value, finally return the computed value. This way the sum in calculated as a typeNext<T> sum and you doesn't have overflow.
The following is a full compiling example
#include <cstdint>
#include <climits>
#include <iostream>
#include <type_traits>
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
int main()
{
auto x = adder(INT_MAX-10, INT_MAX-3);
std::cout << "int: " << sizeof(int)*CHAR_BIT << std::endl;
std::cout << "long: " << sizeof(long)*CHAR_BIT << std::endl;
std::cout << "x: " << sizeof(x)*CHAR_BIT << std::endl;
std::cout << std::is_same<long, decltype(x)>::value << std::endl;
}
In my Linux 64bit platform, i get 32bit for int, 64bit for long and for x and also that long and decltype(x) are the same type.
But this is true for my platform; nothing guaranties that long and decltype(x) are ever the same.
Observe also that trying to get a type for the sum of two std::intmax_t's
std::intmax_t y {};
auto z = adder(y, y);
gives an error and doesn't compile because isn't defined a typeFor for a N bigger that sizeof(std::intmax_t)*CHAR_BIT.

Split parameter pack in 0 ... N-1 and Nth element

I would like to split a parameter pack into the first N - 1 and the Nth parameters without using the typical index_sequence & tuple trick but cannot seem to wrap my head around it, yet I'm pretty sure it should be doable? (getting the last item is easy enough with recursion).
The final function to call looks like
void Fun( Foo a, Bar b );
and a is acquired from a variadic one in turn:
template< class... T >
Foo CalcFoo( T... args );
My current implementation:
//get the last item of the pack
template< class T >
T split_last( T t ){ return t; }
template< class... T >
T split_last( T, T... t ){ return split_last( t... ); }
//helper
template< class... T, size_t... Indices >
Foo CalcFoo( const std::tuple< T... >& args, index_sequence< Indices... > )
{
return CalcFoo( std::get< Indices >( args )... );
}
//split and call
template< class... T >
void MoreFun( T... args )
{
//make a tuple containing all, then get n -1 items out of it
const auto tup = std::make_tuple< T... >( args... );
Fun( CalcFoo( tup, make_index_sequence< sizeof...( T ) - 1 >() ),
split_last( args... ) );
}
update apart from wanting to know how to do this without a tuple just for the sake of it I also asked this because I somehow thought maybe a tuple would cause overhead. Thereby ignoring the premature optimization mantra, which, as usual, turned out to be correct once again. Compiled in release mode with VS2013 both my and Horstling's code yield the exact same assembly code. Everything including CalcFoo is inlined up until the call to Fun. In other words: the tuple is completely gone. So I'll probably stick with this implementation anyway because it's pretty clear.
Ok, let's be creative. I`m sure there is a more "standard" way to do this, but I kinda like this solution ;)
http://coliru.stacked-crooked.com/a/25a3fa276e56cd94
The core idea is to recursively rotate the arguments until we can split out the (formerly) last argument.
template <size_t N>
struct MoreFunHelper
{
template <class Head, class... Tail>
static void RotateLeft(Head head, Tail... tail)
{
MoreFunHelper<N - 1>::RotateLeft(tail..., head);
}
};
template <>
struct MoreFunHelper<0>
{
template <class Head, class... Tail>
static void RotateLeft(Head head, Tail... tail)
{
Fun(CalcFoo(tail...), head);
}
};
template< class... T >
void MoreFun(T... args)
{
MoreFunHelper<sizeof...(T) - 1>::RotateLeft(args...);
}
So if we start with the arguments
1 2 3 4 5
it rotates them 4 times:
2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4
Now we can split them smoothly into [5] and [1 2 3 4], which is exactly what we want. At this point, the recursion stops and just calls the functions CalcFoo and Fun.

Is this a bug with std::underlying_type

I think I may have encountered a bug with the c++11 template std::underlying_type.
I use a traits class to define the ranges of enumerations we have in our system.
I am then able to provide a generic is_valid function.
I recently extended the function when -Wextra was enabled because I was getting
a lot of warnings about an always true comparison.
When an enum is of an unsigned type, and its first value is 0, the warning was generated.
Solved that easily. But the next day some unit tests in modules using the function started
to fail.
When you don't specify the underlying type of the enum, it still chooses the correct implementation, but somehow returns the wrong result.
Here is the minimal example (http://ideone.com/PwFz15):
#include <type_traits>
#include <iostream>
using namespace std;
enum Colour
{
RED = 0,
GREEN,
BLUE
};
enum NoProblems : int
{
A,
B,
C
};
enum AlsoOk : unsigned
{
D,
E,
F
};
template <typename Enum> struct enum_traits;
template <> struct enum_traits<Colour>
{
typedef Colour type;
static constexpr type FIRST = RED;
static constexpr type LAST = BLUE;
};
template <> struct enum_traits<NoProblems>
{
typedef NoProblems type;
static constexpr type FIRST = A;
static constexpr type LAST = C;
};
template <> struct enum_traits<AlsoOk>
{
typedef AlsoOk type;
static constexpr type FIRST = D;
static constexpr type LAST = F;
};
#if 0
// This implementation gives you warnings about an always true comparison
// ONLY IF you define the underlying type of your enum, such as Colour.
template <typename Enum>
inline constexpr bool is_valid(Enum e)
{
return e >= enum_traits<Enum>::FIRST && e <= enum_traits<Enum>::LAST;
}
#endif
// So you define the is_valid function like so, to prevent the warnings:
template <typename Enum, typename enable_if<is_unsigned<typename underlying_type<Enum>::type>::value && enum_traits<Enum>::FIRST == 0, int>::type = 0>
inline constexpr bool is_valid(Enum e)
{
return e <= enum_traits<Enum>::LAST;
}
template <typename Enum, typename enable_if<is_signed<typename underlying_type<Enum>::type>::value || enum_traits<Enum>::FIRST != 0, int>::type = 0>
inline constexpr bool is_valid(Enum e)
{
return e >= enum_traits<Enum>::FIRST && e <= enum_traits<Enum>::LAST;
}
int main()
{
Colour c = static_cast<Colour>(RED - 1);
cout << is_valid(c) << endl;
NoProblems np = static_cast<NoProblems>(A - 1);
cout << is_valid(np) << endl;
AlsoOk ao = static_cast<AlsoOk>(D - 1);
cout << is_valid(ao) << endl;
return 0;
}
Which gives the output:
1
0
0
Clearly the output for the first call to is_valid, should be 0 / false. Somehow the enum is both signed and unsigned at the same time?
Have I missed some critical piece of documentation in the standard library regarding the templates I've used?
It is fixable by performing the comparison like so:
return static_cast<typename std::underlying_type<Enum>::type>(e) <= enum_traits<Enum>::LAST;
But it doesn't seem like that should be necessary.
I've tried this on gcc 4.8.1, gcc 4.7.3 and clang 3.2.1, all on x86-64
C++11 5.2.9 [expr.static.cast]/10:
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is
unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting
value is unspecified (and might not be in that range).
The "range of the enumeration values" is defined in 7.2/7:
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the
underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the
largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K
be 1 for a two’s complement representation and 0 for a one’s complement or sign-magnitude representation.
bmax is the smallest value greater than or equal to max(|emin| − K, |emax|) and equal to 2M − 1, where
M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise. The size of
the smallest bit-field large enough to hold all the values of the enumeration type is max(M, 1) if bmin is
zero and M + 1 otherwise. It is possible to define an enumeration that has values not defined by any of its
enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a
single enumerator with value 0.
For Colour, the range of the enumeration values (assuming two's-complement) is [0, 3]. RED - 1 is either -1 or UINT_MAX, both of which are outside the range [0, 3], so the result of the static_cast is unspecified.
Since the result of converting out-of-range values is unspecified, you would do better to perform your comparisons in the domain of the underlying type, which is exactly the effect of your fix.

Resources