Skip to content

Commit

Permalink
tweaks to span-cast functions
Browse files Browse the repository at this point in the history
Reviewed By: DenisYaroshevskiy

Differential Revision: D62460853

fbshipit-source-id: bbf14a1f7451455f7540b9baa3b7c5a80d8c6367
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Sep 12, 2024
1 parent 1473781 commit 6cd8806
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 103 deletions.
68 changes: 31 additions & 37 deletions folly/container/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,91 +22,85 @@
#include <folly/Portability.h>
#include <folly/portability/Constexpr.h>

#if __cpp_lib_span
#if __cpp_lib_span >= 202002L
#include <span>
#endif

namespace folly {

#if __cpp_lib_span
#if __cpp_lib_span >= 202002L

namespace detail {

struct span_cast_impl_fn {
template <typename U, typename T, std::size_t Extend>
constexpr auto operator()(std::span<T, Extend> in, U* castData) const {
template <typename U, typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in, U* castData) const {
assert(
static_cast<const void*>(in.data()) ==
static_cast<const void*>(castData));
static_cast<void const*>(in.data()) ==
static_cast<void const*>(castData));

// check alignment
if (!folly::is_constant_evaluated_or(true)) {
assert(reinterpret_cast<std::uintptr_t>(in.data()) % sizeof(U) == 0);
}

if constexpr (Extend == std::dynamic_extent) {
if constexpr (Extent == std::dynamic_extent) {
assert(in.size() * sizeof(T) % sizeof(U) == 0);
return std::span<U>(castData, in.size() * sizeof(T) / sizeof(U));
} else {
static_assert(in.size() * sizeof(T) % sizeof(U) == 0);
constexpr std::size_t kResSize = Extend * sizeof(T) / sizeof(U);
static_assert(Extent * sizeof(T) % sizeof(U) == 0);
constexpr std::size_t kResSize = Extent * sizeof(T) / sizeof(U);
return std::span<U, kResSize>(castData, kResSize);
}
}
};

} inline constexpr span_cast_impl;
inline constexpr span_cast_impl_fn span_cast_impl;

} // namespace detail

/**
* static_span_cast
* static_span_cast_fn
* reinterpret_span_cast
* reinterpret_span_cast_fn
* const_span_cast
* const_span_cast_fn
*
* converting a span to a different span.
* (you get a span to the same bytes but treated as different type)
*
* Example:
*
* enum class SomeEnum : int { ... };
*
* std::span<SomeEnum> s = ...
* std::span<int> as_ints = folly::reinterpret_span_cast<int>(s);
*/
/// static_span_cast
/// static_span_cast_fn
/// reinterpret_span_cast
/// reinterpret_span_cast_fn
/// const_span_cast
/// const_span_cast_fn
///
/// Casts a span to a different span. The result is a span referring to the same
/// region in memory but as a different type.
///
/// Example:
///
/// std::span<std::byte> bytes = ...
/// std::span<int> ints = folly::reinterpret_span_cast<int>(bytes);

template <typename U>
struct static_span_cast_fn {
template <typename T, std::size_t Extend>
constexpr auto operator()(std::span<T, Extend> in) const {
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, static_cast<U*>(in.data()));
}
};

template <typename U>
inline constexpr static_span_cast_fn<U> static_span_cast;

template <typename U>
struct reinterpret_span_cast_fn {
template <typename T, std::size_t Extend>
constexpr auto operator()(std::span<T, Extend> in) const {
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, reinterpret_cast<U*>(in.data()));
}
};

template <typename U>
inline constexpr reinterpret_span_cast_fn<U> reinterpret_span_cast;

template <typename U>
struct const_span_cast_fn {
template <typename T, std::size_t Extend>
constexpr auto operator()(std::span<T, Extend> in) const {
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, const_cast<U*>(in.data()));
}
};

template <typename U>
inline constexpr const_span_cast_fn<U> const_span_cast;

Expand Down
130 changes: 64 additions & 66 deletions folly/container/test/span_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,123 +21,121 @@

#include <folly/portability/GTest.h>

#if __cpp_lib_span
#if __cpp_lib_span >= 202002L

template <typename To, typename From, std::size_t Extend = std::dynamic_extent>
template <typename To, typename From>
using reinterpret_span_cast_result_type =
decltype(folly::reinterpret_span_cast<To>(
std::declval<std::span<From, Extend>>()));
decltype(folly::reinterpret_span_cast<To>(std::declval<From>()));

template <typename To, typename From, std::size_t Extend = std::dynamic_extent>
using static_span_cast_result_type = decltype(folly::const_span_cast<To>(
std::declval<std::span<From, Extend>>()));
template <typename To, typename From>
using static_span_cast_result_type =
decltype(folly::const_span_cast<To>(std::declval<From>()));

template <typename To, typename From, std::size_t Extend = std::dynamic_extent>
using const_span_cast_result_type = decltype(folly::const_span_cast<To>(
std::declval<std::span<From, Extend>>()));
template <typename To, typename From>
using const_span_cast_result_type =
decltype(folly::const_span_cast<To>(std::declval<From>()));

static_assert( //
std::is_same_v<
std::span<char>,
reinterpret_span_cast_result_type<char, int>>);
reinterpret_span_cast_result_type<char, std::span<int>>>);

static_assert( //
std::is_same_v<
std::span<const char>,
reinterpret_span_cast_result_type<const char, const int>>);
reinterpret_span_cast_result_type<const char, std::span<const int>>>);
static_assert( //
std::is_same_v<
std::span<const char, 12>,
reinterpret_span_cast_result_type<const char, const int, 3>>);
reinterpret_span_cast_result_type<
const char,
std::span<const int, 3>>>);

static_assert( //
std::is_same_v<
std::span<const char, 12>,
const_span_cast_result_type<const char, char, 12>>);
const_span_cast_result_type<const char, std::span<char, 12>>>);
static_assert( //
std::is_same_v<
std::span<char, 12>,
const_span_cast_result_type<char, const char, 12>>);
const_span_cast_result_type<char, std::span<const char, 12>>>);

static_assert( //
std::is_same_v<
std::span<const char>,
const_span_cast_result_type<const char, char>>);
const_span_cast_result_type<const char, std::span<char>>>);

static_assert( //
std::is_same_v<
std::span<char>,
const_span_cast_result_type<char, const char>>);
const_span_cast_result_type<char, std::span<const char>>>);

static_assert( //
std::is_same_v<
std::span<const char, 12>,
static_span_cast_result_type<const char, char, 12>>);
static_span_cast_result_type<const char, std::span<char, 12>>>);

static_assert( //
std::is_same_v<
std::span<const char>,
static_span_cast_result_type<const char, char>>);
static_span_cast_result_type<const char, std::span<char>>>);

TEST(SpanCast, Examples) {
auto tstSpanCast = [](auto to, auto from) {
struct SpanCastTest : testing::Test {
template <typename To, typename From>
static auto test(To to, From from) {
EXPECT_EQ(
static_cast<const void*>(from.data()),
static_cast<const void*>(to.data()));

EXPECT_EQ(
static_cast<const void*>(from.data() + from.size()),
static_cast<const void*>(to.data() + to.size()));
};

{
std::array<int, 4> a;
tstSpanCast(
folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));
tstSpanCast(
folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));
}
};

{
std::vector<int> a(4u, 1);
tstSpanCast(
folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));
tstSpanCast(
folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));
}
TEST_F(SpanCastTest, array) {
std::array<int, 4> a;
test(folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));
test(folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));
}

{
const std::vector<int> a(4u, 1);
tstSpanCast(folly::const_span_cast<int>(std::span(a)), std::span(a));
TEST_F(SpanCastTest, vector) {
std::vector<int> a(4u, 1);
test(folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));
test(folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));
}

std::vector<int> b(4u, 1);
tstSpanCast(folly::static_span_cast<const int>(std::span(b)), std::span(b));
tstSpanCast(folly::const_span_cast<const int>(std::span(b)), std::span(b));
tstSpanCast(
folly::reinterpret_span_cast<const int>(std::span(b)), std::span(b));
}
TEST_F(SpanCastTest, const_cast) {
const std::vector<int> a(4u, 1);
test(folly::const_span_cast<int>(std::span(a)), std::span(a));
}

// constexpr
{
constexpr bool validation = std::invoke([] {
std::array<int, 4> a{0, 1, 2, 3};
std::span<int, 4> mutableAFixed(a);
std::span<int> mutableADynamic(a);
auto resFixed = folly::static_span_cast<const int>(mutableAFixed);
if (resFixed.data() != mutableAFixed.data() ||
resFixed.size() != mutableAFixed.size()) {
return false;
}
auto resDynamic = folly::static_span_cast<const int>(mutableADynamic);
if (resDynamic.data() != mutableAFixed.data() ||
resDynamic.size() != mutableAFixed.size()) {
return false;
}

return true;
});
EXPECT_TRUE(validation);
}
TEST_F(SpanCastTest, all_casts) {
std::vector<int> b(4u, 1);
test(folly::static_span_cast<const int>(std::span(b)), std::span(b));
test(folly::const_span_cast<const int>(std::span(b)), std::span(b));
test(folly::reinterpret_span_cast<const int>(std::span(b)), std::span(b));
}

TEST_F(SpanCastTest, static_cast_constexpr) {
constexpr bool validation = std::invoke([] {
std::array<int, 4> a{0, 1, 2, 3};
std::span<int, 4> mutableAFixed(a);
std::span<int> mutableADynamic(a);
auto resFixed = folly::static_span_cast<const int>(mutableAFixed);
if (resFixed.data() != mutableAFixed.data() ||
resFixed.size() != mutableAFixed.size()) {
return false;
}
auto resDynamic = folly::static_span_cast<const int>(mutableADynamic);
if (resDynamic.data() != mutableAFixed.data() ||
resDynamic.size() != mutableAFixed.size()) {
return false;
}

return true;
});
EXPECT_TRUE(validation);
}

#endif

0 comments on commit 6cd8806

Please sign in to comment.