Skip to content

Commit

Permalink
Added to_array (#14)
Browse files Browse the repository at this point in the history
* Added to_array

* Suppress unused warnings

* Fix unused warnings (2)

* Workaround msvc 14.0 bug

* Workaround MSVC problems with moving arrays

* MSVC workaround in constexpr test

* Further MSVC fixes

* Disable rvalue tests for MSVC 14.0

* Incorrect argument spec

* constexpr checks

* const test

* Disable rvalues for msvc 14.1

* Docs

* Added more const tests

* Correct static_asserts

* Update type requirements in docs

* Corrected return types and conditions

* Doc updates
  • Loading branch information
anarthal authored Oct 4, 2024
1 parent c49f822 commit d2d5cbe
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/compat/reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ include::invoke.adoc[]
include::latch.adoc[]
include::mem_fn.adoc[]
include::shared_lock.adoc[]
include::to_array.adoc[]
include::type_traits.adoc[]
72 changes: 72 additions & 0 deletions doc/compat/to_array.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
////
Copyright 2024 Ruben Perez Hidalgo
Distributed under the Boost Software License, Version 1.0.
https://www.boost.org/LICENSE_1_0.txt
////

[#to_array]
# <boost/compat/to_array.hpp>
:idprefix: ref_to_array_

## Description

The header `<boost/compat/to_array.hpp>` implements, in a portable way, the C++20
`std::to_array` function, present in the `<array>` header.

`to_array` creates a `std::array` from a single-dimensional C array,
performing an element-wise copy or move.

## Example

```cpp
int input [] = {1, 2, 3};
std::array<int, 3> output = boost::compat::to_array(input);
assert((
output == std::array<int, 3>{{1, 2, 3}}
));
```

## Synopsis

```cpp
namespace boost
{
namespace compat
{

template <class T, std::size_t N>
constexpr std::array<remove_cv_t<T>, N> to_array(T (&a)[N]);

template <class T, std::size_t N>
constexpr std::array<remove_cv_t<T>, N> to_array(T (&&a)[N]);

} // namespace compat
} // namespace boost
```

## to_array

```cpp
template <class T, std::size_t N>
constexpr std::array<remove_cv_t<T>, N> to_array(T (&&a)[N]);
```

[horizontal]
Effects:;; Creates an array of `N` elements by copying elements in `a`.
For every `i` in `0, ..., N-1`, copy-initializes the i-th element
in the output array from `a[i]`.
Type requirements:;; `std::is_constructible_v<remove_cv_t<T>, T&> && !std::is_array_v<T>`.
Otherwise, the overload is ill-formed.


```cpp
template <class T, std::size_t N>
constexpr std::array<remove_cvref_t<T>, N> to_array(T (&a)[N]);
```

[horizontal]
Effects:;; Creates an array of `N` elements by moving elements in `a`.
For every `i` in `0, ..., N-1`, move-initializes the i-th element
in the output array from `std::move(a[i])`.
Type requirements:;; `std::is_constructible_v<remove_cv_t<T>, T&&> && !std::is_array_v<T>`.
Otherwise, the overload is ill-formed.
60 changes: 60 additions & 0 deletions include/boost/compat/to_array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef BOOST_COMPAT_TO_ARRAY_HPP_INCLUDED
#define BOOST_COMPAT_TO_ARRAY_HPP_INCLUDED

// Copyright 2024 Ruben Perez Hidalgo
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/compat/integer_sequence.hpp>
#include <boost/compat/type_traits.hpp>

#include <array>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace boost {
namespace compat {

namespace detail {

template <class T, std::size_t N, std::size_t... I>
constexpr std::array<remove_cv_t<T>, N> to_array_lvalue(T (&a)[N], index_sequence<I...>)
{
return {{a[I]...}};
}

template <class T, std::size_t N, std::size_t... I>
constexpr std::array<remove_cv_t<T>, N> to_array_rvalue(T (&&a)[N], index_sequence<I...>)
{
return {{std::move(a[I])...}};
}

} // namespace detail

template <class T, std::size_t N>
constexpr std::array<remove_cv_t<T>, N> to_array(T (&a)[N])
{
static_assert(
std::is_constructible<remove_cv_t<T>, T&>::value,
"This overload requires the resulting element type to be constructible from T&"
);
static_assert(!std::is_array<T>::value, "to_array does not work for multi-dimensional C arrays");
return detail::to_array_lvalue(a, make_index_sequence<N>{});
}

template <class T, std::size_t N>
constexpr std::array<remove_cv_t<T>, N> to_array(T (&&a)[N])
{
static_assert(
std::is_constructible<remove_cv_t<T>, T&&>::value,
"This overload requires the resulting element type to be constructible from T&&"
);
static_assert(!std::is_array<T>::value, "to_array does not work for multi-dimensional C arrays");
return detail::to_array_rvalue(static_cast<T(&&)[N]>(a), make_index_sequence<N>{});
}

} // namespace compat
} // namespace boost

#endif // BOOST_COMPAT_TO_ARRAY_HPP_INCLUDED
5 changes: 5 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,8 @@ run function_ref_mfn_test.cpp ;
run function_ref_fn_noexcept_test.cpp ;
run function_ref_mfn_noexcept_test.cpp ;
run function_ref_obj_noexcept_test.cpp ;

# MSVC 14.0/14.1 C array rvalue references do not work
run to_array_lvalue_test.cpp ;
run to_array_rvalue_test.cpp : : :
<toolset>msvc-14.0:<build>no <toolset>msvc-14.1:<build>no ;
60 changes: 60 additions & 0 deletions test/to_array_lvalue_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2024 Ruben Perez Hidalgo.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/compat/to_array.hpp>
#include <boost/core/lightweight_test.hpp>

#include <array>
#include <vector>

namespace compat = boost::compat;

int main()
{
{
// regular C array
int input[] = {1, 2};
auto output = compat::to_array(input);
static_assert(std::is_same<decltype(output), std::array<int, 2>>::value, "");
BOOST_TEST_EQ(output[0], 1);
BOOST_TEST_EQ(output[1], 2);
}
{
// regular C array, const
const int input[] = {1, 2};
auto output = compat::to_array(input);
static_assert(std::is_same<decltype(output), std::array<int, 2>>::value, "");
BOOST_TEST_EQ(output[0], 1);
BOOST_TEST_EQ(output[1], 2);
}
{
// values not moved
const std::vector<int> vec{1, 2};
std::vector<int> input[]{vec};
auto output = compat::to_array(input);
static_assert(std::is_same<decltype(output), std::array<std::vector<int>, 1>>::value, "");
BOOST_TEST_ALL_EQ(output[0].begin(), output[0].end(), vec.begin(), vec.end());
BOOST_TEST_ALL_EQ(input[0].begin(), input[0].end(), vec.begin(), vec.end()); // input not modified
}
{
// const values work
const std::vector<int> vec{1, 2};
const std::vector<int> input[]{vec};
auto output = compat::to_array(input);
static_assert(std::is_same<decltype(output), std::array<std::vector<int>, 1>>::value, "");
BOOST_TEST_ALL_EQ(output[0].begin(), output[0].end(), vec.begin(), vec.end());
BOOST_TEST_ALL_EQ(input[0].begin(), input[0].end(), vec.begin(), vec.end()); // input not modified
}
{
// constexpr check
constexpr int input[] = {1, 2, 3};
constexpr auto output = compat::to_array(input);
static_assert(std::is_same<decltype(output), const std::array<int, 3>>::value, "");
BOOST_TEST_EQ(output[0], 1);
BOOST_TEST_EQ(output[1], 2);
BOOST_TEST_EQ(output[2], 3);
}

return boost::report_errors();
}
71 changes: 71 additions & 0 deletions test/to_array_rvalue_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2024 Ruben Perez Hidalgo.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/compat/to_array.hpp>
#include <boost/core/lightweight_test.hpp>

#include <array>
#include <memory>
#include <utility>
#include <vector>

namespace compat = boost::compat;

int main()
{
{
// regular C array
int input[] = {5, 6};
auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), std::array<int, 2>>::value, "");
BOOST_TEST_EQ(output[0], 5);
BOOST_TEST_EQ(output[1], 6);
}
{
// regular C array, const
const int input[] = {5, 6};
auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), std::array<int, 2>>::value, "");
BOOST_TEST_EQ(output[0], 5);
BOOST_TEST_EQ(output[1], 6);
}
{
// values moved
const std::vector<int> vec{1, 2};
std::vector<int> input[]{vec};
auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), std::array<std::vector<int>, 1>>::value, "");
BOOST_TEST_ALL_EQ(output[0].begin(), output[0].end(), vec.begin(), vec.end());
BOOST_TEST(input[0].empty()); // input left in a moved-from state
}
{
// const values work (although they don't result in an effective move)
const std::vector<int> vec{1, 2};
const std::vector<int> input[]{vec};
auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), std::array<std::vector<int>, 1>>::value, "");
BOOST_TEST_ALL_EQ(output[0].begin(), output[0].end(), vec.begin(), vec.end());
BOOST_TEST_ALL_EQ(input[0].begin(), input[0].end(), vec.begin(), vec.end()); // input not modified
}
{
// move-only types work
std::unique_ptr<int> input[] = {std::unique_ptr<int>{new int(42)}};
int* ptr = input[0].get();
auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), std::array<std::unique_ptr<int>, 1>>::value, "");
BOOST_TEST_EQ(output[0].get(), ptr);
BOOST_TEST_EQ(input[0].get(), nullptr); // input left in a moved-from state
}
{
// constexpr check
constexpr int input[] = {1, 2, 3};
constexpr auto output = compat::to_array(std::move(input));
static_assert(std::is_same<decltype(output), const std::array<int, 3>>::value, "");
BOOST_TEST_EQ(output[0], 1);
BOOST_TEST_EQ(output[1], 2);
BOOST_TEST_EQ(output[2], 3);
}

return boost::report_errors();
}

0 comments on commit d2d5cbe

Please sign in to comment.