Create function with unknown number of std::array [duplicate] - c++11

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)...

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.

Why there are two different answer in same compare template function

When I try to implement a template to compare two variables' value.
When I try passing string as parameters then the program couldn't compare the value right.
However when I add two same variables this code get me a right result.
Just as the picture shows.
You passed it a const char * pointer to compare and that will compare the pointer addresses, not the contents with '>'. As these are from different objects/strings, you have no way to know which will be first or last in memory, and it may vary from compile to compile, or even potentially run to run.
As you had the std::string local variable, I assume you intended to pass that, which does have comparison operators to compare the contents. If you want to pass a string literal as an std::string to such a template function, you must do it explicitly, such as:
Max<std::string>("a", "b"); // K is std::string, so both parameters will use the implicit constructor
Max(std::string("a"), std::string("b")); // Explicitly construct strings
If you do want Max to work with char pointers, you might overload or specialise it to use say strcmp, which does compare the contents.
template<class T> T Max(T x, T y)
{
return x > y ? x : y;
}
template<> const char* Max(const char *x, const char *y)
{
return strcmp(x, y) > 0 ? x : y;
}
template<> char* Max(char *x, char *y)
{
return strcmp(x, y) > 0 ? x : y;
}

How can I create an std::array of char array?

I was trying to create a std::array of char array so that I could declare it static constexpr.
eg:
#include <array>
int main(){
static constexpr char A[] = "abc";
static constexpr char B[] = "def";
static constexpr std::array<char[], 3> tmp{A, B};
}
When I do this I get error message " too many initializers for"
By output of your compiler I could surmise that you have a non-standard extension active. Stricly ISO-wise use of char[] as parameter of template is not legal,. Some compilers would treat that as char[0].
what your array meant to store? way you're trying to do it would store adresses of array to char (and only way to do so would be replace char[] by by const char* in template parameters. std::array is trivial class which stores data statically so you cannot implement array with variable length of strings that way.
Either 1) abolish constexpr and use std::string 2) use const char* 3) worst case (which sometimes is the best) - use fixed array length or "naked" 2-dimensional array with aggregate initializer list.
You may use:
static constexpr std::array<char[4], 2> tmp {{{ A[0], A[1], A[2], A[3] },
{ B[0], B[1], B[2], B[3] }}};
or
static constexpr std::array<char[4], 2> tmp { "abc", "def" };
From http://en.cppreference.com/w/cpp/container/array:
When initializing an object of array type, the initializer must be either a string literal (optionally enclosed in braces) or be a brace-enclosed list of initialized for array members.
Thus you cannot initialize an array (member of std::array of char[4]) by an object of another array.
With C++20 std::to_array can be used.
static constexpr char A[] = "abc";
static constexpr char B[] = "def";
static constexpr auto tmp = std::to_array({A, B});

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.

constexpr template functions don't see member array sizes as const expressions

Both clang and gcc fail to compile the code below when ArrayCount is a template. This seems wrong, especially in light of the fact that the sizeof ArrayCount solution work. The template version of ArrayCount is normally a better solution, but it's getting in the way here and constexpr is seemingly not living up to the spirit of its promise.
#if 1
template<typename T, size_t N>
constexpr size_t ArrayCount(T (&)[N])
{
return N;
}
// Results in this (clang): error : static_assert expression is not an integral constant expression
// Results in this (gcc): error: non-constant condition for static assertion, 'this' is not a constant expression
#else
#define ArrayCount(t) (sizeof(t) / sizeof(t[0]))
// Succeeds
#endif
struct X
{
int x[4];
X() { static_assert(ArrayCount(x) == 4, "should never fail"); }
};
The right solution doesn't use homebrew code, but a simple type trait:
int a[] = {1, 2, 3};
#include <type_traits>
static_assert(std::extent<decltype(a)>::value == 3, "You won't see this");
It makes sense to me that this code would fail to compile since ArrayCount is a function taking a non-constexpr argument. According to the standard, I believe this means that ArrayCount must be intstantiated as a non-constexpr function.
There are workarounds, of course. I can think of two off the top of my head (one implemented in terms of the other):
template<typename T> struct ArrayCount;
template<typename T, size_t N>
struct ArrayCount<T[N]> {
static size_t const size = N;
};
template<typename T>
constexpr size_t ArrayCount2() {
return ArrayCount<T>::size;
}
struct X {
int x[4];
X() {
static_assert(ArrayCount<decltype(x)>::size == 4, "should never fail");
static_assert(ArrayCount2<decltype(x)>() == 4, "should never fail");
}
};
It does mean having to use decltype() when you might not wish to, but it does break the pro-forma constraint on taking a non-constexpr parameter.

Resources