mirror of https://github.com/davisking/dlib.git
[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:
parent
7c8ca22580
commit
1925b2d10f
176
dlib/invoke.h
176
dlib/invoke.h
|
@ -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>{}))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue