C++ filling multidimensional std::arrays - c++

To declare a matrix with all of its elements having a certain value, using std::array the only way I know to do so looks like the following:
std::array<std::array<int, dim_1>, dim_2> matrix;
for (auto it = matrix.begin(); it != matrix.end(); ++it)
std::fill(it->begin(), it->end(), number);
Is there a better, more concise way?

auto matrix = std::array<std::array<int, 4>, 4>();
const auto value = 64;
A one-liner:
std::for_each(matrix.begin(), matrix.end(), [value](auto& column) { std::for_each(column.begin(), column.end(), [value](auto& element) {element = value; }); });
Another one:
std::for_each(matrix.begin(), matrix.end(), [value](auto& column) { std::fill(column.begin(), column.end(), value); });
Something actually readable:
for (auto& column : matrix) {
for (auto& element : column) {
element = value;
}
}
I have no idea if those are actually columns though
This compiles on latest MSVC too and looks funny enough:
std::fill(matrix.begin(), matrix.end(), std::array{ value, value, value, value });

If you're looking for conciseness, you can fill it with a filled array:
decltype(matrix[0]) temp;
temp.fill(5);
matrix.fill(temp);
You can technically remove the temporary assuming a non-zero dimension:
matrix[0].fill(5);
matrix.fill(matrix[0]);
In order for this to perform as well as filling in place, you'll have to rely on the compiler seeing through it. Alternatively, you can plop this in a constexpr function since C++20 and guarantee a compile-time result if you wish.

With helper function:
namespace detail
{
template <typename T, std::size_t...Is>
constexpr std::array<T, sizeof...(Is)>
make_array(const T& value, std::index_sequence<Is...>)
{
return {{(static_cast<void>(Is), value)...}};
}
}
template <std::size_t N, typename T>
constexpr std::array<T, N> make_array(const T& value)
{
return detail::make_array(value, std::make_index_sequence<N>());
}
And then
/*const*/ auto matrix = make_array<dim_2>(make_array<dim_1>(value));
Advantage over fill is that it support non-default constructible types.
And advantage of helper function (even if implemented with std::fill) over assignation afterward is that you can initialize const variable :)

Related

Uniform initialization by tuple

Today, I arrived at a situation, where I have a vector of tuples, where the tuples might contain several entries. Now I wanted to convert my vector of tuples to a vector of objects, such that the entries of the tuples will exactly match the uniform initialization of my object.
The following code does the job for me, but it is a bit clumsy. I'm asking myself if it might be possible to derive a generic solution that can construct the Objects if the tuples matches exactly the uniform initialization order of the objects.
This might be a very desirable functionality, when the number of parameters to pass grows.
#include <vector>
#include <tuple>
#include <string>
#include <algorithm>
struct Object
{
std::string s;
int i;
double d;
};
int main() {
std::vector<std::tuple<std::string, int, double>> values = { {"A",0,0.},{"B",1,1.} };
std::vector<Object> objs;
std::transform(values.begin(), values.end(), std::back_inserter(objs), [](auto v)->Object
{
// This might get tedious to type, if the tuple grows
return { std::get<0>(v), std::get<1>(v), std::get<2>(v) };
// This is my desired behavior, but I don't know what magic_wrapper might be
// return magic_wrapper(v);
});
return EXIT_SUCCESS;
}
Provide Object an std::tuple constructor. You can use std::tie to assign your members:
template<typename ...Args>
Object(std::tuple<Args...> t) {
std::tie(s, i, d) = t;
}
Now it gets automatically constructed:
std::transform(values.begin(), values.end(), std::back_inserter(objs),
[](auto v) -> Object {
return { v };
});
To reduce the amount of copying you might want to replace auto v with const auto& v and make the constructor accept a const std::tuple<Args...>& t.
Also, it's good practise to access the source container via const iterator:
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs), ...
Here is a non-intrusive version (i.e. not touching Object) that extracts the number of specified data members. Note that this relies on aggregate initialization.
template <class T, class Src, std::size_t... Is>
constexpr auto createAggregateImpl(const Src& src, std::index_sequence<Is...>) {
return T{std::get<Is>(src)...};
}
template <class T, std::size_t n, class Src>
constexpr auto createAggregate(const Src& src) {
return createAggregateImpl<T>(src, std::make_index_sequence<n>{});
}
You invoke it like this:
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
[](const auto& v)->Object { return createAggregate<Object, 3>(v); });
Or, without the wrapping lambda:
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
createAggregate<Object, 3, decltype(values)::value_type>);
As #Deduplicator pointed out, the above helper templates implement parts of std::apply, which can be used instead.
template <class T>
auto aggregateInit()
{
return [](auto&&... args) { return Object{std::forward<decltype(args)>(args)...}; };
}
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
[](const auto& v)->Object { return std::apply(aggregateInit<Object>(), v); });
Since C++17, you might use std::make_from_tuple:
std::transform(values.begin(),
values.end(),
std::back_inserter(objs),
[](const auto& t)
{
return std::make_from_tuple<Object>(t);
});
Note: requires appropriated constructor for Object.

constexpr vector push_back or how to constexpr all the things

There is a good talk by Jason Turner and Ben Deane from C++Now 2017 called "Constexpr all the things" which also gives a constexpr vector implementation. I was dabbling with the idea myself, for educational purposes. My constexpr vector was pure in the sense that pushing back to it would return a new vector with added element.
During the talk, I saw a push_back implementation tat looks like more or less following:
constexpr void push_back(T const& e) {
if(size_ >= Size)
throw std::range_error("can't use more than Size");
else {
storage_[size_++] = e;
}
}
They were taking the element by value and moving it but, I don't think this is the source of my problems. The thing I want to know is, how this function could be used in a constexpr context? This is not a const member function, it modifies the state. I don think it is possible to do something like
constexpr cv::vector<int> v1;
v1.push_back(42);
And if this is not possible, how could we use this thing in constexpr context and achieve the goal of the task using this vector, namely compile-time JSON parsing?
Here is my version, so that you can see both my new vector returning version and the version from the talk. (Note that performance, perfect forwarding etc. concerns are omitted)
#include <cstdint>
#include <array>
#include <type_traits>
namespace cx {
template <typename T, std::size_t Size = 10>
struct vector {
using iterator = typename std::array<T, Size>::iterator;
using const_iterator = typename std::array<T, Size>::const_iterator;
constexpr vector(std::initializer_list<T> const& l) {
for(auto& t : l) {
if(size_++ < Size)
storage_[size_] = std::move(t);
else
break;
}
}
constexpr vector(vector const& o, T const& t) {
storage_ = o.storage_;
size_ = o.size_;
storage_[size_++] = t;
}
constexpr auto begin() const { return storage_.begin(); }
constexpr auto end() const { return storage_.begin() + size_; }
constexpr auto size() const { return size_; }
constexpr void push_back(T const& e) {
if(size_ >= Size)
throw std::range_error("can't use more than Size");
else {
storage_[size_++] = e;
}
}
std::array<T, Size> storage_{};
std::size_t size_{};
};
}
template <typename T>
constexpr auto make_vector(std::initializer_list<T> const& l) {
return cx::vector<int>{l};
}
template <typename T>
constexpr auto push_back(cx::vector<T> const& o, T const& t) {
return cx::vector<int>{o, t};
}
int main() {
constexpr auto v1 = make_vector({1, 2, 3});
static_assert(v1.size() == 3);
constexpr auto v2 = push_back(v1, 4);
static_assert(v2.size() == 4);
static_assert(std::is_same_v<decltype(v1), decltype(v2)>);
// v1.push_back(4); fails on a constexpr context
}
So, this thing made me realize there is probably something deep that I don' know about constexpr. So, recapping the question; how such a constexpr vector could offer a mutating push_back like that in a constexpr context? Seems like it is not working in a constexpr context right now. If push_back in a constexpr context is not intended to begin with, how can you call it a constexpr vector and use it for compile-time JSON parsing?
Your definition of vector is correct, but you can't modify constexpr objects. They are well and truly constant. Instead, do compile-time calculations inside constexpr functions (the output of which can then be assigned to constexpr objects).
For example, we can write a function range, which produces a vector of numbers from 0 to n. It uses push_back, and we can assign the result to a constexpr vector in main.
constexpr vector<int> range(int n) {
vector<int> v{};
for(int i = 0; i < n; i++) {
v.push_back(i);
}
return v;
}
int main() {
constexpr vector<int> v = range(10);
}
Your return cx::vector<int>{o, t}; will produce a compilation error when o and t are of types cx::vector<T> and T respectively, because those are different types, while all elements of std::initializer_list<T> should be of same type (o is not expanded into a list of its elements).
If you're merely after your 'pure' implementation of push_back, then you can make do with standard arrays:
#include <array>
template <typename T, std::size_t N>
constexpr auto push_back(std::array<T, N> const& oldArr, T const& el) {
std::array<T, N+1> newArr{};
std::copy(begin(oldArr), end(oldArr), begin(newArr));
newArr[N] = el;
return newArr;
}
int main() {
constexpr auto a1 = std::to_array({1, 2, 3});
static_assert(a1.size() == 3);
constexpr auto a2 = push_back(a1, 4);
static_assert(a2.size() == 4);
// This assert will still fail though, because push_back's implementation
// above not only returns new array, but also a new type.
// For example, std::array<int, 3> is not the same type as std::array<int, 4>
//static_assert(std::is_same_v<decltype(a1), decltype(a2)>);
}

C++ Transform a std::tuple<A, A, A...> to a std::vector or std::deque

In the simple parser library I am writing, the results of multiple parsers is combined using std::tuple_cat. But when applying a parser that returns the same result multiple times, it becomes important to transform this tuple into a container like a vector or a deque.
How can this be done? How can any tuple of the kind std::tuple<A>, std::tuple<A, A>, std::tuple<A, A, A> etc be converted into a std::vector<A>?
I think this might be possible using typename ...As and sizeof ...(As), but I am not sure how to create a smaller tuple to call the function recursively. Or how to write an iterative solution that extracts elements from the tuple one by one. (as std::get<n>(tuple) is constructed at compile-time).
How to do this?
With the introduction of std::apply(), this is very straightforward:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems){
return std::vector<T>{std::forward<decltype(elems)>(elems)...};
}, std::forward<Tuple>(tuple));
}
std::apply() is a C++17 function but is implementable in C++14 (see link for possible implementation). As an improvement, you could add either SFINAE or a static_assert that all the types in the Tuple are actually T.
As T.C. points out, this incurs an extra copy of every element, since std::initializer_list is backed by a const array. That's unfortunate. We win some on not having to do boundary checks on every element, but lose some on the copying. The copying ends up being too expensive, an alternative implementation would be:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems) {
using expander = int[];
std::vector<T> result;
result.reserve(sizeof...(elems));
expander{(void(
result.push_back(std::forward<decltype(elems)>(elems))
), 0)...};
return result;
}, std::forward<Tuple>(tuple));
}
See this answer for an explanation of the expander trick. Note that I dropped the leading 0 since we know the pack is non-empty. With C++17, this becomes cleaner with a fold-expression:
return std::apply([](auto&&... elems) {
std::vector<T> result;
result.reserve(sizeof...(elems));
(result.push_back(std::forward<decltype(elems)>(elems)), ...);
return result;
}, std::forward<Tuple>(tuple));
Although still relatively not as nice as the initializer_list constructor. Unfortunate.
Here's one way to do it:
#include <tuple>
#include <algorithm>
#include <vector>
#include <iostream>
template<typename first_type, typename tuple_type, size_t ...index>
auto to_vector_helper(const tuple_type &t, std::index_sequence<index...>)
{
return std::vector<first_type>{
std::get<index>(t)...
};
}
template<typename first_type, typename ...others>
auto to_vector(const std::tuple<first_type, others...> &t)
{
typedef typename std::remove_reference<decltype(t)>::type tuple_type;
constexpr auto s =
std::tuple_size<tuple_type>::value;
return to_vector_helper<first_type, tuple_type>
(t, std::make_index_sequence<s>{});
}
int main()
{
std::tuple<int, int> t{2,3};
std::vector<int> v=to_vector(t);
std::cout << v[0] << ' ' << v[1] << ' ' << v.size() << std::endl;
return 0;
}
Although, this doesn't answer the question completely, This still might be suitable in some cases. Only when the number of elements in tuple is around 5, 6. (And you know the size).
tuple<int, int, int, int> a = make_tuple(1, 2, 3, 4);
auto [p, q, r, s] = a;
vector<int> arr(p, q, r, s); // Now, arr has the same elements as in tuple a
Note that, this is C++ 17 feature. More info here

Use std::vector for std::array initialization

Suppose I have a std::vector of a size known at compile time, and I want to turn that into an std::array. How would I do that? Is there a standard function to do this?
The best solution I have so far is this:
template<class T, std::size_t N, class Indexable, std::size_t... Indices>
std::array<T, N> to_array_1(const Indexable& indexable, std::index_sequence<Indices...>) {
return {{ indexable[Indices]... }};
}
template<class T, std::size_t N, class Indexable>
std::array<T, N> to_array(const Indexable& indexable) {
return to_array_1<T, N>(indexable, std::make_index_sequence<N>());
}
std::array<Foo, 123> initFoos {
std::vector<Foo> lst;
for (unsigned i = 0; i < 123; ++i)
lst.push_back(getNextFoo(lst));
return to_array<Foo, 123>(lst); // passing lst.begin() works, too
}
The application is similar to Populate std::array with non-default-constructible type (no variadic templates): I too have a type which is not default-constructible, so I need to compute the actual values by the time the array gets initialized. However contrary to that question, for me the values are not merely a function of the index, but also of the preceding values. I can build my values much more easily using a loop than a series of function calls. So I construct the elements in a loop and place them in a vector, then I want to use the final state of that vector to initialize the array.
The above seems to compile and work fine, but perhaps there are ways to improve it.
Perhaps I could make clever use of some standard library functionality that I wasn't aware of.
Perhaps I can avoid the helper function somehow.
Perhaps I can somehow formulate this in such a way that it works with move semantics for the elements instead of the copy semantics employed above.
Perhaps I can avoid the random access using operator[], and instead use forward iterator semantics only so it would work for std::set or std::forward_list as input, too.
Perhaps I should stop using std::vector and instead express my goal using std::array<std::optional<T>, N> instead, using C++17 or some equivalent implementation.
Related questions:
Copy std::vector into std::array which doesn't assume a type without default constructor, so copying after default initialization is feasible there but not for me.
Populate std::array with non-default-constructible type (no variadic templates) which computes each element from its index independently, so it tries to avoid an intermediate container. Answers use variadic templates even though the title requests avoiding them.
I would propose:
template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{ ((void)Is, *iter++)... }};
}
template<std::size_t N, typename Iter,
typename T = typename std::iterator_traits<Iter>::value_type>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
return to_array<T>(iter, std::make_index_sequence<N>{});
}
This deduces the element type from the iterator and leaves copy-vs-move semantics up to the caller – if the caller wants to move, they can opt-in via std::move_iterator or the like:
auto initFoos() {
constexpr unsigned n{123};
std::vector<Foo> lst;
for (unsigned i{}; i != n; ++i) {
lst.push_back(getNextFoo(lst));
}
// copy-init array elements
return to_array<n>(lst.cbegin());
// move-init array elements
return to_array<n>(std::make_move_iterator(lst.begin()));
}
Online Demo
EDIT: If one wants to override the deduced element type, as indicated in the comments, then I propose:
template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{ ((void)Is, T(*iter++))... }};
}
template<std::size_t N, typename U = void, typename Iter,
typename V = typename std::iterator_traits<Iter>::value_type,
typename T = std::conditional_t<std::is_same<U, void>{}, V, U>>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
return to_array<T>(iter, std::make_index_sequence<N>{});
}
This leaves the element type optional but makes it the second parameter rather than the first, so usage would look like to_array<N, Bar>(lst.begin()) rather than to_array<Bar, N>(lst.begin()).

Swap values and indexes of a vector

I have a std::vector<int> with contiguous shuffled values from 0 to N and want to swap, as efficiently as possible, each value with its position in the vector.
Example:
v[6] = 3;
becomes
v[3] = 6;
This is a simple problem, but I do not know how to handle it in order to make it trivial and, above all, very fast. Thank you very much for your suggestions.
Given N at compile time and given the array contains each index in [0,N) exactly once,
it's relatively straight forward (as long as it doesn't have to be in-place, as mentioned in the comments above) :
Construct a new array so that v'[n] = find_index(v, n) and assign it to the old one.
Here I used variadic templates with std::index_sequence to roll it into a single assignment:
template<typename T, std::size_t N>
std::size_t find_index(const std::array<T,N>& arr, std::size_t index) {
return static_cast<std::size_t>(std::distance(arr.begin(), std::find(arr.begin(), arr.end(), index)));
}
template<typename T, std::size_t N, std::size_t... Index>
void swap_index_value(std::array<T,N>& arr, std::index_sequence<Index...> seq){
arr = { find_index(arr, Index)... };
}
template<typename Integer, std::size_t N>
void swap_index_value(std::array<Integer,N>& arr) {
swap_index_value(arr, std::make_index_sequence<N>{});
}
The complexity of this is does not look great though. Calling find_index(arr, n) for each n in [0,N)
will take N * (N+1) / 2 comparisons total (std::sort would only take N * log(N)).
However, since we know each index is present in the array, we could just fill out an array of indices
as we walk over the original array, and assuming T is an integral type we can skip some std::size_t <-> T conversions, too:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::array<T, N> indices;
for (T i = 0; i < N; ++i)
indices[arr[i]] = i;
arr = indices;
}
We're still using twice the space and doing some randomly ordered writes to our array,
but essentially we're down to 2*N assignments, and the code is simpler than before.
Alternatively, we could also std::sort if we keep a copy to do lookups in:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::sort(arr.begin(), arr.end(), [copy = arr](const T& lhs, const T& rhs) {
return copy[lhs] < copy[rhs];
});
}
First version here,
second version here,
std::sort version here
Benchmarking which one is faster is left as an exercise to the reader ;)