Skip to content

Commit

Permalink
folly::span_cast
Browse files Browse the repository at this point in the history
Summary: a simple utility to convert span types to a different span.

Differential Revision: D61202580
  • Loading branch information
Denis Yaroshevskiy authored and facebook-github-bot committed Aug 14, 2024
1 parent 475b862 commit 53ffd56
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions folly/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ cpp_library(
"//folly/lang:thunk",
"//folly/memory:malloc",
"//folly/portability:config",
"//folly/portability:constexpr",
"//folly/portability:malloc",
],
)
Expand Down
57 changes: 57 additions & 0 deletions folly/Memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@
#include <folly/lang/Thunk.h>
#include <folly/memory/Malloc.h>
#include <folly/portability/Config.h>
#include <folly/portability/Constexpr.h>
#include <folly/portability/Malloc.h>

#if __cpp_lib_span
#include <span>
#endif

namespace folly {

#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
Expand Down Expand Up @@ -845,4 +850,56 @@ template <typename Value, typename T>
struct AllocatorHasDefaultObjectDestroy<std::allocator<Value>, T>
: std::true_type {};

#if __cpp_lib_span

namespace detail {

template <typename U, typename T, std::size_t Extend>
constexpr auto span_cast_impl(std::span<T, Extend> in, U* 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) {
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);
return std::span<U, kResSize>(castData, kResSize);
}
}

} // namespace detail

/**
* 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);
*/

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

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

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

#endif

} // namespace folly
1 change: 1 addition & 0 deletions folly/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include <cassert>
#include <cstdint>
#include <functional>
#include <limits>
Expand Down
116 changes: 116 additions & 0 deletions folly/test/MemoryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,119 @@ TEST(allocateOverAligned, defaultOverCustomAlloc) {
folly::deallocateOverAligned(a, p, 1);
EXPECT_EQ(folly::allocationBytesForOverAligned<decltype(a)>(1), 128);
}

#if __cpp_lib_span

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

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

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

TEST(Traits, SpanCast) {
auto tstSpanCast = [](auto to, auto from) {
ASSERT_EQ(
static_cast<const void*>(from.data()),
static_cast<const void*>(to.data()));

ASSERT_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));
}

{
const std::vector<int> a(4u, 1);
tstSpanCast(folly::const_span_cast<int>(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));
}

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

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

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

// constexpr
{
[[maybe_unused]] constexpr auto _ = [] {
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);
(void)resFixed;
auto resDynamic = folly::static_span_cast<const int>(mutableADynamic);
(void)resDynamic;
return 0;
}();
}
}

#endif

0 comments on commit 53ffd56

Please sign in to comment.