mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Replace SFINAE with concepts (#5361)
Concepts are cleaner to use and result in much better error messages for incorrect template use.
This commit is contained in:
18
wpiutil/src/main/native/include/wpi/DecayedDerivedFrom.h
Normal file
18
wpiutil/src/main/native/include/wpi/DecayedDerivedFrom.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "wpi/concepts.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
template <class Derived, class Base>
|
||||
concept DecayedDerivedFrom =
|
||||
std::derived_from<std::decay_t<Derived>, std::decay_t<Base>> &&
|
||||
std::convertible_to<std::decay_t<Derived>*, std::decay_t<Base>*>;
|
||||
|
||||
} // namespace wpi
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "wpi/concepts.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
struct empty_array_t {};
|
||||
@@ -26,11 +28,10 @@ class array : public std::array<T, N> {
|
||||
public:
|
||||
constexpr explicit array(empty_array_t) {}
|
||||
|
||||
template <typename... Ts>
|
||||
template <std::convertible_to<T>... Ts>
|
||||
requires(1 + sizeof...(Ts) == N)
|
||||
constexpr array(T arg, Ts&&... args) // NOLINT
|
||||
: std::array<T, N>{std::forward<T>(arg), std::forward<Ts>(args)...} {
|
||||
static_assert(1 + sizeof...(args) == N, "Dimension mismatch");
|
||||
}
|
||||
: std::array<T, N>{std::forward<T>(arg), std::forward<Ts>(args)...} {}
|
||||
|
||||
constexpr array(const array<T, N>&) = default;
|
||||
constexpr array& operator=(const array<T, N>&) = default;
|
||||
@@ -56,33 +57,32 @@ class array : public std::array<T, N> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
array(T, Ts...) -> array<std::enable_if_t<(std::is_same_v<T, Ts> && ...), T>,
|
||||
1 + sizeof...(Ts)>;
|
||||
template <typename T, std::convertible_to<T>... Ts>
|
||||
array(T, Ts...) -> array<T, 1 + sizeof...(Ts)>;
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
template <size_t I, typename T, size_t N>
|
||||
requires(I < N)
|
||||
constexpr T& get(wpi::array<T, N>& arr) noexcept {
|
||||
static_assert(I < N, "array index is within bounds");
|
||||
return std::get<I>(static_cast<std::array<T, N>>(arr));
|
||||
}
|
||||
|
||||
template <size_t I, typename T, size_t N>
|
||||
requires(I < N)
|
||||
constexpr T&& get(wpi::array<T, N>&& arr) noexcept {
|
||||
static_assert(I < N, "array index is within bounds");
|
||||
return std::move(std::get<I>(arr));
|
||||
}
|
||||
|
||||
template <size_t I, typename T, size_t N>
|
||||
requires(I < N)
|
||||
constexpr const T& get(const wpi::array<T, N>& arr) noexcept {
|
||||
static_assert(I < N, "array index is within bounds");
|
||||
return std::get<I>(static_cast<std::array<T, N>>(arr));
|
||||
}
|
||||
|
||||
template <size_t I, typename T, size_t N>
|
||||
requires(I < N)
|
||||
constexpr const T&& get(const wpi::array<T, N>&& arr) noexcept {
|
||||
static_assert(I < N, "array index is within bounds");
|
||||
return std::move(std::get<I>(arr));
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ struct tuple_size<wpi::array<T, N>> : public integral_constant<size_t, N> {};
|
||||
|
||||
// Partial specialization for wpi::array
|
||||
template <size_t I, typename T, size_t N>
|
||||
requires(I < N)
|
||||
struct tuple_element<I, wpi::array<T, N>> {
|
||||
static_assert(I < N, "index is out of bounds");
|
||||
using type = T;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
57
wpiutil/src/main/native/include/wpi/concepts.h
Normal file
57
wpiutil/src/main/native/include/wpi/concepts.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
#if defined(__APPLE__) && !defined(__cpp_lib_concepts)
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename T, typename... Args>
|
||||
concept constructible_from =
|
||||
is_nothrow_destructible_v<T> && is_constructible_v<T, Args...>;
|
||||
|
||||
template <typename From, typename To>
|
||||
concept convertible_to = is_convertible_v<From, To> &&
|
||||
requires { static_cast<To>(declval<From>()); };
|
||||
|
||||
template <typename T>
|
||||
concept move_constructible = constructible_from<T, T> && convertible_to<T, T>;
|
||||
|
||||
template <typename T>
|
||||
concept copy_constructible =
|
||||
move_constructible<T> && constructible_from<T, T&> &&
|
||||
convertible_to<T&, T> && constructible_from<T, const T&> &&
|
||||
convertible_to<const T&, T> && constructible_from<T, const T> &&
|
||||
convertible_to<const T, T>;
|
||||
|
||||
template <typename T>
|
||||
concept default_initializable =
|
||||
constructible_from<T> && requires { T{}; } && requires { ::new T; };
|
||||
|
||||
template <typename Derived, typename Base>
|
||||
concept derived_from =
|
||||
is_base_of_v<Base, Derived> &&
|
||||
is_convertible_v<const volatile Derived*, const volatile Base*>;
|
||||
|
||||
template <typename T>
|
||||
concept floating_point = is_floating_point_v<T>;
|
||||
|
||||
template <typename T>
|
||||
concept integral = is_integral_v<T>;
|
||||
|
||||
template <typename F, typename... Args>
|
||||
concept invocable = requires(F&& f, Args&&... args) {
|
||||
invoke(forward<F>(f), forward<Args>(args)...);
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // defined(__APPLE__) && !defined(__cpp_lib_concepts)
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -20,6 +19,7 @@
|
||||
#include "wpi/SmallString.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/StringExtras.h"
|
||||
#include "wpi/concepts.h"
|
||||
#include "wpi/mutex.h"
|
||||
#include "wpi/raw_ostream.h"
|
||||
|
||||
@@ -219,9 +219,8 @@ class JArrayRefInner<C, jbyte> {
|
||||
template <typename C>
|
||||
class JArrayRefInner<C, jlong> {
|
||||
public:
|
||||
template <typename U,
|
||||
typename = std::enable_if_t<sizeof(U) == sizeof(jlong) &&
|
||||
std::is_integral_v<U>>>
|
||||
template <typename U>
|
||||
requires(sizeof(U) == sizeof(jlong) && std::integral<U>)
|
||||
operator std::span<const U>() const { // NOLINT
|
||||
auto arr = static_cast<const C*>(this)->array();
|
||||
if (arr.empty()) {
|
||||
@@ -397,46 +396,38 @@ inline jstring MakeJString(JNIEnv* env, std::string_view str) {
|
||||
// details for MakeJIntArray
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Slow path (get primitive array and set individual elements).
|
||||
*
|
||||
* This is used if the input type is not an integer of the same size (note
|
||||
* signed/unsigned is ignored).
|
||||
*/
|
||||
template <typename T,
|
||||
bool = (std::is_integral<T>::value && sizeof(jint) == sizeof(T))>
|
||||
template <typename T>
|
||||
struct ConvertIntArray {
|
||||
static jintArray ToJava(JNIEnv* env, std::span<const T> arr) {
|
||||
jintArray jarr = env->NewIntArray(arr.size());
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
if constexpr (sizeof(T) == sizeof(jint) && std::integral<T>) {
|
||||
// Fast path (use SetIntArrayRegion).
|
||||
jintArray jarr = env->NewIntArray(arr.size());
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
env->SetIntArrayRegion(jarr, 0, arr.size(),
|
||||
reinterpret_cast<const jint*>(arr.data()));
|
||||
return jarr;
|
||||
} else {
|
||||
// Slow path (get primitive array and set individual elements).
|
||||
//
|
||||
// This is used if the input type is not an integer of the same size (note
|
||||
// signed/unsigned is ignored).
|
||||
jintArray jarr = env->NewIntArray(arr.size());
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
jint* elements =
|
||||
static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
|
||||
if (!elements) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
elements[i] = static_cast<jint>(arr[i]);
|
||||
}
|
||||
env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
|
||||
return jarr;
|
||||
}
|
||||
jint* elements =
|
||||
static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
|
||||
if (!elements) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < arr.size(); ++i) {
|
||||
elements[i] = static_cast<jint>(arr[i]);
|
||||
}
|
||||
env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
|
||||
return jarr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fast path (use SetIntArrayRegion).
|
||||
*/
|
||||
template <typename T>
|
||||
struct ConvertIntArray<T, true> {
|
||||
static jintArray ToJava(JNIEnv* env, std::span<const T> arr) {
|
||||
jintArray jarr = env->NewIntArray(arr.size());
|
||||
if (!jarr) {
|
||||
return nullptr;
|
||||
}
|
||||
env->SetIntArrayRegion(jarr, 0, arr.size(),
|
||||
reinterpret_cast<const jint*>(arr.data()));
|
||||
return jarr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -574,9 +565,9 @@ WPI_JNI_MAKEJARRAY(jdouble, Double)
|
||||
|
||||
#undef WPI_JNI_MAKEJARRAY
|
||||
|
||||
template <class T, typename = std::enable_if_t<
|
||||
sizeof(typename T::value_type) == sizeof(jlong) &&
|
||||
std::is_integral_v<typename T::value_type>>>
|
||||
template <class T>
|
||||
requires(sizeof(typename T::value_type) == sizeof(jlong) &&
|
||||
std::integral<typename T::value_type>)
|
||||
inline jlongArray MakeJLongArray(JNIEnv* env, const T& arr) {
|
||||
jlongArray jarr = env->NewLongArray(arr.size());
|
||||
if (!jarr) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/concepts.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/**
|
||||
@@ -22,11 +24,9 @@ namespace wpi {
|
||||
*/
|
||||
template <typename T, typename Sequence = std::vector<T>,
|
||||
typename Compare = std::less<typename Sequence::value_type>>
|
||||
requires std::same_as<T, typename Sequence::value_type>
|
||||
class priority_queue {
|
||||
public:
|
||||
static_assert(std::is_same_v<T, typename Sequence::value_type>,
|
||||
"value_type must be the same as the underlying container");
|
||||
|
||||
using value_type = typename Sequence::value_type;
|
||||
using reference = typename Sequence::reference;
|
||||
using const_reference = typename Sequence::const_reference;
|
||||
@@ -34,10 +34,9 @@ class priority_queue {
|
||||
using container_type = Sequence;
|
||||
using value_compare = Compare;
|
||||
|
||||
template <typename Seq = Sequence,
|
||||
typename Requires = typename std::enable_if_t<
|
||||
std::is_default_constructible<Compare>{} &&
|
||||
std::is_default_constructible<Seq>{}>>
|
||||
template <typename Seq = Sequence>
|
||||
requires std::default_initializable<Compare> &&
|
||||
std::default_initializable<Seq>
|
||||
priority_queue() {}
|
||||
|
||||
priority_queue(const Compare& comp, const Sequence& c) : c(c), comp(comp) {
|
||||
|
||||
Reference in New Issue
Block a user