[INVOKE] added is_invocable_r, invoke_r, made everything constexpr and conditionally noexcept (#2458)

* - added dlib::invoke_r()
- added dlib::is_invocable_r<>
- made everything constexpr and conditionally noexcept !
- added tests
This has required a refactor of dlib::invoke since std::mem_fn is not constexpr in c++11

* - !std::is_function replaced with std::is_object. Let's be a bit more precise

* - made dlib::apply constexpr and conditionally noexcept

Co-authored-by: pf <pf@me>
This commit is contained in:
pfeatherstone 2021-11-14 21:18:06 +00:00 committed by GitHub
parent 7c8ca22580
commit 1925b2d10f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 260 additions and 12 deletions

View File

@ -11,32 +11,153 @@ namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace detail {
template< typename F, typename ... Args >
auto INVOKE(F&& fn, Args&& ... args)
template< typename T >
struct is_reference_wrapper : std::false_type {};
template< typename U >
struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};
template <
typename Base,
typename T,
typename Derived,
typename... Args
>
constexpr auto INVOKE(
T Base::*pmf, //pointer to member function
Derived&& ref,
Args&&... args
)
noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)))
-> typename std::enable_if<
std::is_member_pointer<typename std::decay<F>::type>::value,
decltype(std::mem_fn(fn)(std::forward<Args>(args)...))>::type
std::is_function<T>::value &&
std::is_base_of<Base, typename std::decay<Derived>::type>::value,
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))
>::type
{
return std::mem_fn(fn)(std::forward<Args>(args)...) ;
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}
template< typename F, typename ... Args >
auto INVOKE(F&& fn, Args&& ... args)
template<
typename Base,
typename T,
typename RefWrap,
typename... Args
>
constexpr auto INVOKE(
T Base::*pmf, //pointer to member function
RefWrap&& ref,
Args&&... args
)
noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
-> typename std::enable_if<
std::is_function<T>::value &&
is_reference_wrapper<typename std::decay<RefWrap>::type>::value,
decltype((ref.get().*pmf)(std::forward<Args>(args)...))>::type
{
return (ref.get().*pmf)(std::forward<Args>(args)...);
}
template<
typename Base,
typename T,
typename Ptr,
typename... Args
>
constexpr auto INVOKE(
T Base::*pmf, //pointer to member function
Ptr&& ptr,
Args&&... args
)
noexcept(noexcept(((*std::forward<Ptr>(ptr)).*pmf)( std::forward<Args>( args )...)))
-> typename std::enable_if<
std::is_function<T>::value &&
!is_reference_wrapper<typename std::decay<Ptr>::type>::value &&
!std::is_base_of<Base, typename std::decay<Ptr>::type>::value,
decltype(((*std::forward<Ptr>(ptr)).*pmf)(std::forward<Args>(args)...))>::type
{
return ((*std::forward<Ptr>(ptr)).*pmf)( std::forward<Args>( args )...);
}
template<
typename Base,
typename T,
typename Derived
>
constexpr auto INVOKE(
T Base::*pmd, //pointer to member data
Derived&& ref
)
noexcept(noexcept(std::forward<Derived>(ref).*pmd))
-> typename std::enable_if<
std::is_object<T>::value &&
std::is_base_of<Base, typename std::decay<Derived>::type>::value,
decltype(std::forward<Derived>(ref).*pmd)>::type
{
return std::forward<Derived>(ref).*pmd;
}
template<
typename Base,
typename T,
typename RefWrap
>
constexpr auto INVOKE(
T Base::*pmd, //pointer to member data
RefWrap&& ref
)
noexcept(noexcept(ref.get().*pmd))
-> typename std::enable_if<
std::is_object<T>::value &&
is_reference_wrapper<typename std::decay<RefWrap>::type>::value,
decltype(ref.get().*pmd)>::type
{
return ref.get().*pmd;
}
template<
typename Base,
typename T,
typename Ptr
>
constexpr auto INVOKE(
T Base::*pmd, //pointer to member data
Ptr&& ptr
)
noexcept(noexcept((*std::forward<Ptr>(ptr)).*pmd))
-> typename std::enable_if<
std::is_object<T>::value &&
!is_reference_wrapper<typename std::decay<Ptr>::type>::value &&
!std::is_base_of<Base, typename std::decay<Ptr>::type>::value,
decltype((*std::forward<Ptr>(ptr)).*pmd)>::type
{
return (*std::forward<Ptr>(ptr)).*pmd;
}
template<
typename F,
typename... Args
>
constexpr auto INVOKE(
F && f,
Args&&... args
)
noexcept(noexcept(std::forward<F>( f )( std::forward<Args>( args )...)))
-> typename std::enable_if<
!std::is_member_pointer<typename std::decay<F>::type>::value,
decltype(std::forward<F>(fn)(std::forward<Args>(args)...))>::type
decltype(std::forward<F>(f)(std::forward<Args>(args)...))>::type
{
return std::forward<F>(fn)(std::forward<Args>(args)...);
return std::forward<F>( f )( std::forward<Args>( args )...);
}
}
template< typename F, typename... Args>
auto invoke(F && f, Args &&... args)
constexpr auto invoke(F && f, Args &&... args)
/*!
ensures
- identical to std::invoke(std::forward<F>(f), std::forward<Args>(args)...)
- works with C++11 onwards
!*/
noexcept(noexcept(detail::INVOKE(std::forward<F>( f ), std::forward<Args>( args )...)))
-> decltype(detail::INVOKE(std::forward<F>( f ), std::forward<Args>( args )...))
{
return detail::INVOKE(std::forward<F>( f ), std::forward<Args>( args )...);
@ -88,10 +209,38 @@ namespace dlib
// ----------------------------------------------------------------------------------------
template <typename R, typename F, typename... Args>
struct is_invocable_r : std::integral_constant<bool, dlib::is_invocable<F, Args...>::value &&
std::is_convertible<invoke_result_t<F, Args...>, R>::value> {};
/*!
ensures
- identical to std::is_invocable_r<R, F, Args..>
- works with C++11 onwards
!*/
// ----------------------------------------------------------------------------------------
template< typename R, typename F, typename... Args>
constexpr typename std::enable_if<dlib::is_invocable_r<R, F, Args...>::value, R>::type
invoke_r(F && f, Args &&... args)
/*!
ensures
- identical to std::invoke_r<R>(std::forward<F>(f), std::forward<Args>(args)...)
- works with C++11 onwards
!*/
noexcept(noexcept(dlib::invoke(std::forward<F>( f ), std::forward<Args>( args )...)))
{
return dlib::invoke(std::forward<F>( f ), std::forward<Args>( args )...);
}
// ----------------------------------------------------------------------------------------
namespace detail
{
template<typename F, typename Tuple, std::size_t... I>
auto apply_impl(F&& fn, Tuple&& tpl, index_sequence<I...>)
constexpr auto apply_impl(F&& fn, Tuple&& tpl, index_sequence<I...>)
noexcept(noexcept(dlib::invoke(std::forward<F>(fn),
std::get<I>(std::forward<Tuple>(tpl))...)))
-> decltype(dlib::invoke(std::forward<F>(fn),
std::get<I>(std::forward<Tuple>(tpl))...))
{
@ -101,12 +250,15 @@ namespace dlib
}
template<typename F, typename Tuple>
auto apply(F&& fn, Tuple&& tpl)
constexpr auto apply(F&& fn, Tuple&& tpl)
/*!
ensures
- identical to std::apply(std::forward<F>(f), std::forward<Tuple>(tpl))
- works with C++11 onwards
!*/
noexcept(noexcept(detail::apply_impl(std::forward<F>(fn),
std::forward<Tuple>(tpl),
make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{})))
-> decltype(detail::apply_impl(std::forward<F>(fn),
std::forward<Tuple>(tpl),
make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{}))

View File

@ -234,6 +234,100 @@ namespace
// ----------------------------------------------------------------------------------------
const char* func_return_c_string()
{
return "hello darkness my old friend";
}
struct obj_return_c_string
{
obj_return_c_string() = default;
obj_return_c_string(const obj_return_c_string& rhs) = delete;
obj_return_c_string(obj_return_c_string&& rhs) = delete;
const char* run()
{
return "i've come to talk with you again";
}
};
void test_invoke_r()
{
{
static_assert(dlib::is_invocable_r<std::string, decltype(func_return_c_string)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(func_return_c_string);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "hello darkness my old friend");
}
{
obj_return_c_string obj;
static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "i've come to talk with you again");
}
{
obj_return_c_string obj;
static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(&obj)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, &obj);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "i've come to talk with you again");
}
{
auto obj = std::make_shared<obj_return_c_string>();
static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "i've come to talk with you again");
}
{
std::unique_ptr<obj_return_c_string> obj(new obj_return_c_string());
static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "i've come to talk with you again");
}
{
auto lambda_return_c_string = [] {
return "because a vision softly creeping";
};
static_assert(dlib::is_invocable_r<std::string, decltype(lambda_return_c_string)>::value, "should be invocable");
auto str = dlib::invoke_r<std::string>(lambda_return_c_string);
static_assert(std::is_same<decltype(str), std::string>::value, "bad return type");
DLIB_TEST(str == "because a vision softly creeping");
}
}
// ----------------------------------------------------------------------------------------
constexpr int multiply_ints(int i, int j)
{
return i*j;
}
struct constexpr_object
{
constexpr int multiply_ints(int i, int j) const
{
return i*j;
}
};
void test_constexpr()
{
static_assert(dlib::invoke(multiply_ints, 2, 5) == 10, "this should be constexpr");
static_assert(dlib::invoke_r<long>(multiply_ints, 2, 5) == 10, "this should be constexpr");
constexpr constexpr_object constexpr_obj;
static_assert(dlib::invoke(&constexpr_object::multiply_ints, constexpr_obj, 2, 5) == 10);
static_assert(dlib::invoke_r<long>(&constexpr_object::multiply_ints, constexpr_obj, 2, 5) == 10);
}
// ----------------------------------------------------------------------------------------
class invoke_tester : public tester
{
@ -251,6 +345,8 @@ namespace
test_member_functions_and_data();
test_return_types();
test_make_from_tuple();
test_invoke_r();
test_constexpr();
}
} a;
}