/* *****************************************************************************
* kblib is a general utility library for C++14 and C++17, intended to provide
* performant high-level abstractions and more expressive ways to do simple
* things.
*
* Copyright (c) 2021 killerbee
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* ****************************************************************************/
/**
* @file
* @brief This header provides some features of C++17 and other
* headers for C++14, as well as some other traits.
*
* @author killerbee
* @date 2019-2021
* @copyright GNU General Public Licence v3.0
*/
#ifndef KBLIB_FAKESTD_H
#define KBLIB_FAKESTD_H
#include "tdecl.h"
#include
#include
#include
#include
#include
#if __has_include("gsl/pointers")
# include "gsl/pointers"
#endif
#ifndef KBLIB_FAKESTD
# define KBLIB_FAKESTD (__cplusplus < 201703L)
#endif
namespace KBLIB_NS {
template
using enable_if_t = typename std::enable_if::type;
template
using decay_t = typename std::decay::type;
template
using remove_cvref_t =
typename std::remove_reference::type>::type;
template
using bool_constant = std::integral_constant;
#if __cpp_lib_constexpr_functional
using std::invoke;
#else
# if __cpp_lib_apply
template
constexpr auto invoke(F&& f, Args&&... args) noexcept(noexcept(std::apply(
std::forward(f), std::forward_as_tuple(std::forward(args)...))))
-> decltype(auto) {
return std::apply(std::forward(f),
std::forward_as_tuple(std::forward(args)...));
}
# else
namespace detail {
template >::value,
int> = 0>
constexpr auto do_invoke(F&& f, Args&&... args) noexcept(noexcept(
std::forward(f)(std::forward(args)...))) -> decltype(auto) {
return std::forward(f)(std::forward(args)...);
}
template >::value
and std::is_member_function_pointer::value,
int> = 0>
constexpr auto do_invoke(F f, Object&& obj, Args&&... args) noexcept(
noexcept((std::forward(obj).*f)(std::forward(args)...)))
-> decltype(auto) {
return (obj.*f)(std::forward(args)...);
}
template ::value
and std::is_member_function_pointer::value,
int> = 0>
constexpr auto do_invoke(F f, Pointer ptr, Args&&... args) noexcept(
noexcept((ptr->*f)(std::forward(args)...))) -> decltype(auto) {
return (ptr->*f)(std::forward(args)...);
}
template >::value
and std::is_member_object_pointer::value,
int> = 0>
constexpr auto do_invoke(Member mem, Object&& obj) noexcept
-> decltype(auto) {
return std::forward(obj).*mem;
}
template ::value
and std::is_member_object_pointer::value,
int> = 0>
constexpr auto do_invoke(Member mem, Pointer ptr) noexcept
-> decltype(auto) {
return ptr.*mem;
}
} // namespace detail
template
constexpr auto invoke(F&& f, Args&&... args) noexcept(noexcept(
detail::do_invoke(std::forward(f), std::forward(args)...)))
-> decltype(auto) {
return detail::do_invoke(std::forward(f), std::forward(args)...);
}
# endif // __cpp_lib_apply
#endif // __cpp_lib_constexpr_functional
/**
* @namespace kblib::fakestd
* @brief A namespace which holds all the C++14 implementations of C++17
* standard library features. In C++17, it is simply defined as an alias to std.
*/
#if KBLIB_FAKESTD
namespace fakestd { // C++14 implementation of C++17 void_t, invoke_result,
// (partially) is_invocable, and is_nothrow_swappable
using std::swap;
/**
* @namespace kblib::fakestd::detail
* @brief Implementation details for kblib::fakestd features
*
* @internal
*/
namespace detail {
template
struct invoke_result {};
template
struct invoke_result(),
std::declval()...))),
F, Args...> {
using type
= decltype(invoke(std::declval(), std::declval()...));
};
} // namespace detail
template
struct invoke_result : detail::invoke_result {};
template
using invoke_result_t = typename invoke_result::type;
template
struct make_void {
typedef void type;
};
template
using void_t = typename make_void::type;
namespace detail {
// ALL generic swap overloads MUST already have a declaration available at
// this point.
struct nat {
nat() = delete;
nat(const nat&) = delete;
nat& operator=(const nat&) = delete;
~nat() = delete;
};
struct two {
char lx[2];
};
struct is_referenceable_impl {
template
static Tp& test(int);
template
static two test(...);
};
template
struct is_referenceable
: std::integral_constant<
bool,
not std::is_same(0)),
two>::value> {};
template ::value and not std::is_void::value>
struct swappable_with {
template
static decltype(swap(std::declval(), std::declval()))
test_swap(int);
template
static nat test_swap(long);
// Extra parens are needed for the C++03 definition of decltype.
using swap1 = decltype((test_swap(0)));
using swap2 = decltype((test_swap(0)));
static const bool value = not std::is_same::value
and not std::is_same::value;
};
template
struct swappable_with : std::false_type {};
template ::value>
struct nothrow_swappable_with {
static const bool value = noexcept(
swap(std::declval(),
std::declval()))and noexcept(swap(std::declval(),
std::declval()));
};
template
struct nothrow_swappable_with : std::false_type {};
} // namespace detail
template
struct is_swappable
: public std::integral_constant::value> {};
template
struct is_nothrow_swappable
: public std::integral_constant<
bool, detail::nothrow_swappable_with::value> {};
# if KBLIB_USE_CXX17
template
struct is_swappable_with
: public std::integral_constant::value> {
};
template
struct is_swappable
: public std::conditional<
detail::is_referenceable::value,
is_swappable_with::type,
typename std::add_lvalue_reference::type>,
std::false_type>::type {};
template
struct is_nothrow_swappable_with
: public integral_constant<
bool, detail::nothrow_swappable_with::value> {};
template
struct is_nothrow_swappable
: public conditional<
detail::is_referenceable::value,
is_nothrow_swappable_with::type,
typename add_lvalue_reference::type>,
false_type>::type {};
template
constexpr bool is_swappable_with_v = is_swappable_with::value;
template
constexpr bool is_swappable_v = is_swappable::value;
template
constexpr bool is_nothrow_swappable_with_v
= is_nothrow_swappable_with::value;
template
constexpr bool is_nothrow_swappable_v = is_nothrow_swappable::value;
# endif
namespace detail {
template
struct not_fn_t {
constexpr explicit not_fn_t(F&& f)
: fd(std::forward(f)) {}
constexpr not_fn_t(const not_fn_t&) = default;
constexpr not_fn_t(not_fn_t&&) = default;
template
constexpr auto operator()(Args&&... args) & -> decltype(
not std::declval&, Args...>>()) {
return not invoke(fd, std::forward(args)...);
}
template
constexpr auto operator()(Args&&... args) const& -> decltype(
not std::declval<
invoke_result_t const&, Args...>>()) {
return not invoke(std::move(fd), std::forward(args)...);
}
std::decay_t fd;
};
} // namespace detail
template
auto not_fn(F&& f) -> detail::not_fn_t {
return detail::not_fn_t(std::forward(f));
}
struct in_place_t {
explicit in_place_t() = default;
};
static constexpr in_place_t in_place{};
template
constexpr auto max_element(ForwardIt first, ForwardIt last) -> ForwardIt {
if (first == last)
return last;
ForwardIt largest = first;
++first;
for (; first != last; ++first) {
if (*largest < *first) {
largest = first;
}
}
return largest;
}
template
constexpr auto max_element(ForwardIt first, ForwardIt last, Compare comp)
-> ForwardIt {
if (first == last)
return last;
ForwardIt largest = first;
++first;
for (; first != last; ++first) {
if (comp(*largest, *first)) {
largest = first;
}
}
return largest;
}
template
constexpr auto size(const C& c) -> decltype(c.size()) {
return c.size();
}
template
constexpr auto size(const T (&)[N]) noexcept -> std::size_t {
return N;
}
// Adapted from libstdc++ code, licensed under GPL
namespace detail {
// invokable
template
struct invokable_r {
template
static auto try_call(int)
-> decltype(kblib::invoke(std::declval(),
std::declval()...));
template
static auto try_call(...) -> detail::nat;
using Result = decltype(try_call(0));
using type = typename std::conditional<
not std::is_same::value,
typename std::conditional::value, std::true_type,
std::is_convertible>::type,
std::false_type>::type;
static const bool value = type::value;
};
template
using invokable = invokable_r;
template
struct nothrow_invokable_r_imp {
static const bool value = false;
};
template
struct nothrow_invokable_r_imp {
using ThisT = nothrow_invokable_r_imp;
template
static void test_noexcept(Tp) noexcept;
static const bool value = noexcept(ThisT::test_noexcept(
kblib::invoke(std::declval(), std::declval()...)));
};
template
struct nothrow_invokable_r_imp {
static const bool value = noexcept(
kblib::invoke(std::declval(), std::declval()...));
};
template
using nothrow_invokable_r
= nothrow_invokable_r_imp::value,
std::is_void::value, Ret, Fp, Args...>;
template
using nothrow_invokable
= nothrow_invokable_r_imp::value, true, void,
Fp, Args...>;
template
struct invoke_of
: public std::enable_if<
invokable::value,
typename invokable_r::Result> {};
} // namespace detail
// is_invocable
template
struct is_invocable
: std::integral_constant::value> {};
template
struct is_invocable_r
: std::integral_constant::value> {
};
template
constexpr bool is_invocable_v = is_invocable::value;
template
constexpr bool is_invocable_r_v = is_invocable_r::value;
// is_nothrow_invocable
template
struct is_nothrow_invocable
: std::integral_constant::value> {
};
template
struct is_nothrow_invocable_r
: std::integral_constant<
bool, detail::nothrow_invokable_r::value> {};
template
constexpr bool is_nothrow_invocable_v
= is_nothrow_invocable::value;
template
constexpr bool is_nothrow_invocable_r_v
= is_nothrow_invocable_r::value;
} // namespace fakestd
#else
namespace fakestd = std;
#endif
using fakestd::is_invocable;
using fakestd::is_invocable_v;
using fakestd::is_invocable_r;
using fakestd::is_invocable_r_v;
using fakestd::is_nothrow_invocable;
using fakestd::is_nothrow_invocable_v;
using fakestd::is_nothrow_invocable_r;
using fakestd::is_nothrow_invocable_r_v;
template
struct meta_type {};
template
struct meta_type {
using type = T;
};
template
using meta_type_t = typename meta_type::type;
template
struct void_if {};
template <>
struct void_if : meta_type {};
template
using void_if_t = typename void_if::type;
using fakestd::void_t;
// metafunction_success:
// SFINAE detector for a ::type member type
template
struct metafunction_success : std::false_type {};
template
struct metafunction_success> : std::true_type {};
template
struct is_callable : metafunction_success> {};
template
using metafunction_value_t
= std::integral_constant;
/**
* @brief Essentially just like std::enable_if, but with a different name that
* makes it clearer what it does in the context of return type SFINAE.
*/
template
struct return_assert {};
template
struct return_assert : meta_type {};
template
using return_assert_t = typename return_assert::type;
namespace detail {
template
struct apply_impl {
template
constexpr static auto do_apply(F&& f, Arg&& arg) noexcept(
noexcept(kblib::invoke(std::forward(f),
std::get(std::forward(arg))...)))
-> decltype(auto) {
return kblib::invoke(std::forward(f),
std::get(std::forward(arg))...);
}
};
} // namespace detail
template
constexpr auto apply(F&& f, Arg&& arg) noexcept(
noexcept(detail::apply_impl::do_apply(
std::forward(f), std::forward(arg),
std::index_sequence::value>{})))
-> decltype(auto) {
return detail::apply_impl::do_apply(
std::forward(f), std::forward(arg),
std::index_sequence::value>{});
}
template
KBLIB_NODISCARD auto to_unique(gsl::owner p) -> std::unique_ptr {
return std::unique_ptr(p);
}
template
KBLIB_NODISCARD auto to_unique(gsl::owner p, D&& d)
-> std::unique_ptr {
return std::unique_ptr(p, d);
}
/**
* @brief Cast integral argument to corresponding unsigned type
*/
template
KBLIB_NODISCARD constexpr auto to_unsigned(I x) -> std::make_unsigned_t {
return static_cast>(x);
}
/**
* @brief Cast integral argument to corresponding signed type
*/
template
KBLIB_NODISCARD constexpr auto to_signed(I x) -> std::make_signed_t {
return static_cast>(x);
}
/**
* @brief Cast argument to equivalently-sized type with the same signednessas
* the template parameter
*/
template
KBLIB_NODISCARD constexpr auto signed_cast(F x)
-> enable_if_t::value and std::is_integral::value
and std::is_signed::value,
std::make_signed_t> {
return to_signed(x);
}
/**
* @brief Cast argument to equivalently-sized type with the same signednessas
* the template parameter
*/
template
KBLIB_NODISCARD constexpr auto signed_cast(F x)
-> enable_if_t::value and std::is_integral::value
and std::is_unsigned::value,
std::make_unsigned_t> {
return to_unsigned(x);
}
template
struct has_member_swap {
private:
using yes = char (&)[1];
using no = char (&)[2];
template
static auto check(decltype(&C::swap)) -> yes;
template
static auto check(...) -> no;
public:
constexpr static bool value = sizeof(check(nullptr)) == sizeof(yes);
};
template
struct is_tuple_like : std::false_type {};
template
struct is_tuple_like::type>>
: std::true_type {};
namespace detail {
template
constexpr auto ignore(Ts&&... /*unused*/) noexcept -> void {}
template
constexpr auto
swap_tuple_impl(T& a, T& b, std::index_sequence /*unused*/) noexcept(
noexcept(ignore(((void)swap(std::get(a), std::get(b)), 0)...)))
-> void {
ignore(((void)swap(std::get(a), std::get(b)), 0)...);
}
} // namespace detail
KBLIB_UNUSED struct {
/**
* @brief Swaps two objects, using move operations.
*
* @param a,b The objects that will be swapped.
*/
template ::value
and not is_tuple_like::value,
int> = 0>
KBLIB_UNUSED constexpr auto operator()(T& a, T& b) const
noexcept(std::is_nothrow_move_constructible::value and
std::is_nothrow_move_assignable::value) -> void {
auto tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
return;
}
/**
* @brief Swaps two objects, using a member swap function, if detected.
*
* @param a,b The objects that will be swapped.
*/
template ::value, int> = 0>
KBLIB_UNUSED constexpr auto operator()(T& a, T& b) const
noexcept(noexcept(a.swap(b))) -> void {
a.swap(b);
return;
}
/**
* @brief Swaps two arrays elementwise.
*
* @param a,b The arrays that will be swapped.
*/
template
KBLIB_UNUSED constexpr auto operator()(T (&a)[N], T (&b)[N]) const
noexcept(std::is_nothrow_move_constructible::value and
std::is_nothrow_move_assignable::value) -> void {
for (std::size_t i = 0; i < N; ++i) {
swap(a[i], b[i]);
}
}
/**
* @brief Swaps two tuples elementwise.
*
* @param a,b The tuples that will be swapped.
*/
template ::value
and not has_member_swap::value,
std::size_t>
N
= std::tuple_size