add kari::detail::std_ext::invoke function

This commit is contained in:
2017-11-20 02:54:39 +07:00
parent db734ce122
commit 4fb30d304b
3 changed files with 249 additions and 41 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
build-tests-*
tests/build/*
tests/.vscode/*
tests/CMakeLists.txt.user

206
kari.hpp
View File

@@ -24,47 +24,6 @@ namespace kari
template < typename... Ts >
using void_t = typename make_void<Ts...>::type;
//
// apply
//
template < typename F, typename Tuple, std::size_t... I >
constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template < typename F, typename Tuple >
constexpr decltype(auto) apply(F&& f, Tuple&& args) {
return apply_impl(
std::forward<F>(f),
std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
//
// is_invocable, is_invocable_v
//
namespace detail
{
template < typename F, typename = void >
struct is_invocable_impl
: std::false_type {};
template < typename F, typename... Args >
struct is_invocable_impl<
F(Args...),
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>
> : std::true_type {};
}
template < typename F, typename... Args >
struct is_invocable
: detail::is_invocable_impl<F(Args...)> {};
template < typename F, typename... Args >
constexpr bool is_invocable_v = is_invocable<F, Args...>::value;
//
// conjunction, conjunction_v
//
@@ -120,6 +79,171 @@ namespace kari
template < typename B >
constexpr bool negation_v = negation<B>::value;
//
// is_reference_wrapper, is_reference_wrapper_v
//
namespace detail
{
template < typename T >
struct is_reference_wrapper_impl
: public std::false_type {};
template < typename T >
struct is_reference_wrapper_impl<std::reference_wrapper<T>>
: public std::true_type {};
}
template < typename T >
struct is_reference_wrapper
: public detail::is_reference_wrapper_impl<std::remove_cv_t<T>> {};
template < typename T >
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value;
//
// invoke
//
namespace detail
{
//
// invoke_member_object_impl
//
template
<
typename Base, typename F, typename Derived,
typename std::enable_if<std::is_base_of<Base, std::decay_t<Derived>>::value, int>::type = 0
>
constexpr decltype(auto) invoke_member_object_impl(F Base::* f, Derived&& ref) {
return std::forward<Derived>(ref).*f;
}
template
<
typename Base, typename F, typename RefWrap,
typename std::enable_if<is_reference_wrapper<std::decay_t<RefWrap>>::value, int>::type = 0
>
constexpr decltype(auto) invoke_member_object_impl(F Base::* f, RefWrap&& ref) {
return ref.get().*f;
}
template
<
typename Base, typename F, typename Pointer,
typename std::enable_if<conjunction_v<
negation<std::is_base_of<Base, std::decay_t<Pointer>>>,
negation<is_reference_wrapper<std::decay_t<Pointer>>>
>, int>::type = 0
>
constexpr decltype(auto) invoke_member_object_impl(F Base::* f, Pointer&& ptr) {
return (*std::forward<Pointer>(ptr)).*f;
}
//
// invoke_member_function_impl
//
template
<
typename Base, typename F, typename Derived, typename... Args,
typename std::enable_if<std::is_base_of<Base, std::decay_t<Derived>>::value, int>::type = 0
>
constexpr decltype(auto) invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args) {
return (std::forward<Derived>(ref).*f)(std::forward<Args>(args)...);
}
template
<
typename Base, typename F, typename RefWrap, typename... Args,
typename std::enable_if<is_reference_wrapper<std::decay_t<RefWrap>>::value, int>::type = 0
>
constexpr decltype(auto) invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args) {
return (ref.get().*f)(std::forward<Args>(args)...);
}
template
<
typename Base, typename F, typename Pointer, typename... Args,
typename std::enable_if<conjunction_v<
negation<std::is_base_of<Base, std::decay_t<Pointer>>>,
negation<is_reference_wrapper<std::decay_t<Pointer>>>
>, int>::type = 0
>
constexpr decltype(auto) invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args) {
return ((*std::forward<Pointer>(ptr)).*f)(std::forward<Args>(args)...);
}
}
template
<
typename F, typename... Args,
typename std::enable_if<!std::is_member_pointer<std::decay_t<F>>::value, int>::type = 0
>
constexpr decltype(auto) invoke(F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template
<
typename F, typename T,
typename std::enable_if<std::is_member_object_pointer<std::decay_t<F>>::value, int>::type = 0
>
constexpr decltype(auto) invoke(F&& f, T&& t) {
return detail::invoke_member_object_impl(std::forward<F>(f), std::forward<T>(t));
}
template
<
typename F, typename... Args,
typename std::enable_if<std::is_member_function_pointer<std::decay_t<F>>::value, int>::type = 0
>
constexpr decltype(auto) invoke(F&& f, Args&&... args) {
return detail::invoke_member_function_impl(std::forward<F>(f), std::forward<Args>(args)...);
}
//
// apply
//
template < typename F, typename Tuple, std::size_t... I >
constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template < typename F, typename Tuple >
constexpr decltype(auto) apply(F&& f, Tuple&& args) {
return apply_impl(
std::forward<F>(f),
std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
//
// is_invocable, is_invocable_v
//
namespace detail
{
template < typename F, typename = void >
struct is_invocable_impl
: std::false_type {};
template < typename F, typename... Args >
struct is_invocable_impl<
F(Args...),
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>
> : std::true_type {};
}
template < typename F, typename... Args >
struct is_invocable
: detail::is_invocable_impl<F(Args...)> {};
template < typename F, typename... Args >
constexpr bool is_invocable_v = is_invocable<F, Args...>::value;
}
}
}

View File

@@ -588,6 +588,89 @@ TEST_CASE("kari") {
}
}
TEST_CASE("kari_details") {
SECTION("invoke") {
using kari::detail::std_ext::invoke;
REQUIRE(invoke(std::minus<>(), 44, 2) == 42);
REQUIRE(invoke(&box::addV, box(10), 2) == 12);
{
auto b1 = box(10);
const auto b2 = box(10);
REQUIRE(invoke(&box::addV, b1, 2) == 12);
REQUIRE(invoke(&box::v, b2) == 10);
REQUIRE(invoke(&box::addV, &b1, 2) == 14);
REQUIRE(invoke(&box::v, &b2) == 10);
}
{
auto b1 = box(10);
const auto b2 = box(10);
REQUIRE(invoke(&box::addV, std::ref(b1), 2) == 12);
REQUIRE(invoke(&box::v, std::ref(b2)) == 10);
}
{
struct box2 : box {
box2(int v) : box(v) {}
};
auto b1 = box2(10);
const auto b2 = box2(10);
REQUIRE(invoke(&box::addV, b1, 2) == 12);
REQUIRE(invoke(&box::v, b2) == 10);
REQUIRE(invoke(&box::addV, &b1, 2) == 14);
REQUIRE(invoke(&box::v, &b2) == 10);
REQUIRE(invoke(&box2::addV, b1, 2) == 16);
REQUIRE(invoke(&box2::v, b2) == 10);
REQUIRE(invoke(&box2::addV, &b1, 2) == 18);
REQUIRE(invoke(&box2::v, &b2) == 10);
}
{
struct box2 : box {
box2(int v) : box(v) {}
};
auto b1 = box2(10);
const auto b2 = box2(10);
REQUIRE(invoke(&box::addV, std::ref(b1), 2) == 12);
REQUIRE(invoke(&box::v, std::ref(b2)) == 10);
REQUIRE(invoke(&box2::addV, std::ref(b1), 2) == 14);
REQUIRE(invoke(&box2::v, std::ref(b2)) == 10);
}
{
struct box2 : box {
box2(int v) : box(v), ov(v) {}
int ov;
};
auto b1 = box2(10);
const auto b2 = box2(10);
REQUIRE(invoke(&box2::ov, box2(10)) == 10);
REQUIRE(invoke(&box2::ov, b1) == 10);
REQUIRE(invoke(&box2::ov, b2) == 10);
REQUIRE(invoke(&box2::ov, &b1) == 10);
REQUIRE(invoke(&box2::ov, &b2) == 10);
REQUIRE(invoke(&box2::ov, std::ref(b1)) == 10);
REQUIRE(invoke(&box2::ov, std::ref(b2)) == 10);
}
{
struct box2 : box {
box2(int v) : box(v), ov(v) {}
int ov;
};
struct box3 : box2 {
box3(int v) : box2(v) {}
};
auto b1 = box3(10);
const auto b2 = box3(10);
REQUIRE(invoke(&box2::ov, box3(10)) == 10);
REQUIRE(invoke(&box2::ov, b1) == 10);
REQUIRE(invoke(&box2::ov, b2) == 10);
REQUIRE(invoke(&box2::ov, &b1) == 10);
REQUIRE(invoke(&box2::ov, &b2) == 10);
REQUIRE(invoke(&box2::ov, std::ref(b1)) == 10);
REQUIRE(invoke(&box2::ov, std::ref(b2)) == 10);
}
}
}
TEST_CASE("kari_helpers") {
SECTION("fid") {
REQUIRE(fid(box(10)).v() == 10);