mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
[INVOKE] C++11 backport of std::invoke, std::invoke_result, std:apply and std::make_from_tuple (#2450)
* added backport of std::invoke, std::invoke_result and std::apply * added backport of std::invoke, std::invoke_result and std::apply * msvc doesn't like keyword 'not' * i think this fixes detection of invoke on MSVC * ok, i think detection of invoke stuff is fixed on windows * - just have dlib's own implementation and don't use standard library even if c++17 is enabled. - added tests for dlib::invoke_result_t * added docs * - added dlib::make_from_tuple - added tests + docs * - make sure you use the dlib:: namespace. Otherwise, when compiling with C++17, compiler might get confused - use remove_reference instead of decay. That's what the standard says to use * added dlib::is_invocable * - defined invoke_traits. This removes dupplicate code. - This makes absolutely no difference but is just a tiny bit nicer. * removed the test that could potentially fail with MSVC Co-authored-by: pfeatherstone <peter@me>
This commit is contained in:
parent
7f6746e7cd
commit
f77189db03
145
dlib/invoke.h
Normal file
145
dlib/invoke.h
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright (C) 2016 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_INVOKE_Hh_
|
||||
#define DLIB_INVOKE_Hh_
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include "utility.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
namespace detail {
|
||||
template< typename F, typename ... Args >
|
||||
auto INVOKE(F&& fn, 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
|
||||
{
|
||||
return std::mem_fn(fn)(std::forward<Args>(args)...) ;
|
||||
}
|
||||
|
||||
template< typename F, typename ... Args >
|
||||
auto INVOKE(F&& fn, 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
|
||||
{
|
||||
return std::forward<F>(fn)(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template< typename F, typename... Args>
|
||||
auto invoke(F && f, Args &&... args)
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::invoke(std::forward<F>(f), std::forward<Args>(args)...)
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
-> decltype(detail::INVOKE(std::forward<F>( f ), std::forward<Args>( args )...))
|
||||
{
|
||||
return detail::INVOKE(std::forward<F>( f ), std::forward<Args>( args )...);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template< typename AlwaysVoid, typename, typename...>
|
||||
struct invoke_traits
|
||||
{
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template< typename F, typename... Args >
|
||||
struct invoke_traits< decltype( void(dlib::invoke(std::declval<F>(), std::declval<Args>()...)) ), F, Args...>
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
using type = decltype( dlib::invoke(std::declval<F>(), std::declval<Args>()...) );
|
||||
};
|
||||
}
|
||||
|
||||
template< typename F, typename... Args >
|
||||
struct invoke_result : detail::invoke_traits< void, F, Args...> {};
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::invoke_result<F, Args..>
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
|
||||
template< typename F, typename... Args >
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::invoke_result_t<F, Args..>
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template< typename F, typename... Args >
|
||||
struct is_invocable : std::integral_constant<bool, detail::invoke_traits< void, F, Args...>::value> {};
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::is_invocable<F, Args..>
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename F, typename Tuple, std::size_t... I>
|
||||
auto apply_impl(F&& fn, Tuple&& tpl, index_sequence<I...>)
|
||||
-> decltype(dlib::invoke(std::forward<F>(fn),
|
||||
std::get<I>(std::forward<Tuple>(tpl))...))
|
||||
{
|
||||
return dlib::invoke(std::forward<F>(fn),
|
||||
std::get<I>(std::forward<Tuple>(tpl))...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Tuple>
|
||||
auto apply(F&& fn, Tuple&& tpl)
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::apply(std::forward<F>(f), std::forward<Tuple>(tpl))
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
-> 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>{}))
|
||||
{
|
||||
return detail::apply_impl(std::forward<F>(fn),
|
||||
std::forward<Tuple>(tpl),
|
||||
make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class T, class Tuple, std::size_t... I>
|
||||
constexpr T make_from_tuple_impl( Tuple&& t, index_sequence<I...> )
|
||||
{
|
||||
return T(std::get<I>(std::forward<Tuple>(t))...);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class Tuple>
|
||||
constexpr T make_from_tuple( Tuple&& t )
|
||||
/*!
|
||||
ensures
|
||||
- identical to std::make_from_tuple<T>(std::forward<Tuple>(t))
|
||||
- works with C++11 onwards
|
||||
!*/
|
||||
{
|
||||
return detail::make_from_tuple_impl<T>(std::forward<Tuple>(t),
|
||||
make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
#endif //DLIB_INVOKE_Hh_
|
@ -71,6 +71,7 @@ set (tests
|
||||
hash_table.cpp
|
||||
hog_image.cpp
|
||||
image.cpp
|
||||
invoke.cpp
|
||||
iosockstream.cpp
|
||||
is_same_object.cpp
|
||||
isotonic_regression.cpp
|
||||
|
256
dlib/test/invoke.cpp
Normal file
256
dlib/test/invoke.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright (C) 2008 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <dlib/invoke.h>
|
||||
#include "tester.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace test;
|
||||
using namespace dlib;
|
||||
|
||||
logger dlog("test.invoke");
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
static const std::string run1_str1 = "hello there 1";
|
||||
static const std::string run1_str2 = "hello there 2";
|
||||
static const std::string run1_str3 = "hello there 3";
|
||||
static const std::string run1_str4 = "hello there 4";
|
||||
static const std::string run1_str5 = "hello there 5";
|
||||
|
||||
void func_testargs(int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4)
|
||||
{
|
||||
DLIB_TEST(i > 0);
|
||||
DLIB_TEST(ref1 == run1_str1);
|
||||
DLIB_TEST(ref2 == run1_str2);
|
||||
DLIB_TEST(ref3 == run1_str3);
|
||||
DLIB_TEST(ref4 == run1_str4);
|
||||
ref4 = run1_str5;
|
||||
}
|
||||
|
||||
int func_return_addition(int i, int j)
|
||||
{
|
||||
return i + j;
|
||||
}
|
||||
|
||||
void test_functions()
|
||||
{
|
||||
static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, const std::string&, const std::string&, std::string&>::value, "should be invocable!");
|
||||
static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, const std::string&, std::string&>::value, "should be invocable!");
|
||||
static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, std::string, std::string&>::value, "should be invocable!");
|
||||
static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, std::string, std::reference_wrapper<std::string>>::value, "should be invocable!");
|
||||
|
||||
{
|
||||
std::string str = run1_str4;
|
||||
dlib::invoke(func_testargs, 1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str));
|
||||
DLIB_TEST(str == run1_str5);
|
||||
}
|
||||
|
||||
{
|
||||
std::string str = run1_str4;
|
||||
dlib::apply(func_testargs, std::make_tuple(1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str)));
|
||||
DLIB_TEST(str == run1_str5);
|
||||
}
|
||||
|
||||
{
|
||||
for (int i = -10 ; i <= 10 ; i++)
|
||||
{
|
||||
for (int j = -10 ; j <= 10 ; j++)
|
||||
{
|
||||
DLIB_TEST(dlib::invoke(func_return_addition, i, j) == (i+j));
|
||||
DLIB_TEST(dlib::apply(func_return_addition, std::make_tuple(i, j)) == (i+j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
void test_lambdas()
|
||||
{
|
||||
{
|
||||
std::string str = run1_str4;
|
||||
dlib::invoke([](int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4) {
|
||||
DLIB_TEST(i > 0);
|
||||
DLIB_TEST(ref1 == run1_str1);
|
||||
DLIB_TEST(ref2 == run1_str2);
|
||||
DLIB_TEST(ref3 == run1_str3);
|
||||
DLIB_TEST(ref4 == run1_str4);
|
||||
ref4 = run1_str5;
|
||||
}, 1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str));
|
||||
DLIB_TEST(str == run1_str5);
|
||||
}
|
||||
|
||||
{
|
||||
std::string str = run1_str4;
|
||||
dlib::apply([](int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4) {
|
||||
DLIB_TEST(i > 0);
|
||||
DLIB_TEST(ref1 == run1_str1);
|
||||
DLIB_TEST(ref2 == run1_str2);
|
||||
DLIB_TEST(ref3 == run1_str3);
|
||||
DLIB_TEST(ref4 == run1_str4);
|
||||
ref4 = run1_str5;
|
||||
}, std::make_tuple(1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str)));
|
||||
DLIB_TEST(str == run1_str5);
|
||||
}
|
||||
|
||||
{
|
||||
for (int i = -10 ; i <= 10 ; i++)
|
||||
{
|
||||
for (int j = -10 ; j <= 10 ; j++)
|
||||
{
|
||||
DLIB_TEST(dlib::invoke([](int i, int j) {return i + j;}, i, j) == (i+j));
|
||||
DLIB_TEST(dlib::apply([](int i, int j) {return i + j;}, std::make_tuple(i,j)) == (i+j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
struct example_struct
|
||||
{
|
||||
example_struct(int i_ = 0) : i(i_) {}
|
||||
example_struct(const example_struct&) = delete;
|
||||
example_struct& operator=(const example_struct&) = delete;
|
||||
example_struct(example_struct&& other) : i(other.i) {other.i = 0;}
|
||||
example_struct& operator=(example_struct&& other) {i = other.i; other.i = 0; return *this;}
|
||||
|
||||
int get_i() const {return i;}
|
||||
|
||||
int i = 0;
|
||||
};
|
||||
|
||||
void test_member_functions_and_data()
|
||||
{
|
||||
example_struct obj1(10);
|
||||
std::unique_ptr<example_struct> obj2(new example_struct(11));
|
||||
std::shared_ptr<example_struct> obj3(new example_struct(12));
|
||||
|
||||
DLIB_TEST(dlib::invoke(&example_struct::get_i, obj1) == 10);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::i, obj1) == 10);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::get_i, &obj1) == 10);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::i, &obj1) == 10);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::get_i, obj2) == 11);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::i, obj2) == 11);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::get_i, obj3) == 12);
|
||||
DLIB_TEST(dlib::invoke(&example_struct::i, obj3) == 12);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
int return_int()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int& return_int_ref()
|
||||
{
|
||||
static int i = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
const int& return_int_const_ref()
|
||||
{
|
||||
static const int i = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
int* return_int_pointer()
|
||||
{
|
||||
static int i = 0;
|
||||
return &i;
|
||||
}
|
||||
|
||||
const int* return_int_const_pointer()
|
||||
{
|
||||
static const int i = 0;
|
||||
return &i;
|
||||
}
|
||||
|
||||
void test_return_types()
|
||||
{
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(return_int)>>::value, "bad type");
|
||||
static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(return_int_ref)>>::value, "bad type");
|
||||
static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(return_int_const_ref)>>::value, "bad type");
|
||||
static_assert(std::is_same<int*, dlib::invoke_result_t<decltype(return_int_pointer)>>::value, "bad type");
|
||||
static_assert(std::is_same<const int*, dlib::invoke_result_t<decltype(return_int_const_pointer)>>::value, "bad type");
|
||||
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), const example_struct&>>::value, "bad type");
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), example_struct&>>::value, "bad type");
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), const example_struct*>>::value, "bad type");
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), example_struct*>>::value, "bad type");
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), std::unique_ptr<example_struct>>>::value, "bad type");
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), std::shared_ptr<example_struct>>>::value, "bad type");
|
||||
|
||||
static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(&example_struct::i), const example_struct&>>::value, "bad type");
|
||||
static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), example_struct&>>::value, "bad type");
|
||||
static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(&example_struct::i), const example_struct*>>::value, "bad type");
|
||||
static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), example_struct*>>::value, "bad type");
|
||||
static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), std::unique_ptr<example_struct>>>::value, "bad type");
|
||||
static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), std::shared_ptr<example_struct>>>::value, "bad type");
|
||||
|
||||
auto lambda_func_return_int = []() -> int {return 0;};
|
||||
static_assert(std::is_same<int, dlib::invoke_result_t<decltype(lambda_func_return_int)>>::value, "bad type");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
void test_make_from_tuple()
|
||||
{
|
||||
struct multi_args_object
|
||||
{
|
||||
multi_args_object(int i_, int j_) : i(i_), j(j_) {}
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
};
|
||||
|
||||
{
|
||||
auto obj = dlib::make_from_tuple<multi_args_object>(std::make_tuple(1, 2));
|
||||
static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type");
|
||||
DLIB_TEST(obj.i == 1);
|
||||
DLIB_TEST(obj.j == 2);
|
||||
}
|
||||
|
||||
{
|
||||
std::array<int,2> a = {3, 4};
|
||||
auto obj = dlib::make_from_tuple<multi_args_object>(a);
|
||||
static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type");
|
||||
DLIB_TEST(obj.i == 3);
|
||||
DLIB_TEST(obj.j == 4);
|
||||
}
|
||||
|
||||
{
|
||||
auto obj = dlib::make_from_tuple<multi_args_object>(std::make_pair(5, 6));
|
||||
static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type");
|
||||
DLIB_TEST(obj.i == 5);
|
||||
DLIB_TEST(obj.j == 6);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class invoke_tester : public tester
|
||||
{
|
||||
public:
|
||||
invoke_tester(
|
||||
) : tester("test_invoke",
|
||||
"Runs tests on dlib::invoke and dlib::apply")
|
||||
{}
|
||||
|
||||
void perform_test(
|
||||
)
|
||||
{
|
||||
test_functions();
|
||||
test_lambdas();
|
||||
test_member_functions_and_data();
|
||||
test_return_types();
|
||||
test_make_from_tuple();
|
||||
}
|
||||
} a;
|
||||
}
|
42
dlib/utility.h
Normal file
42
dlib/utility.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2016 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_UTILITY_Hh_
|
||||
#define DLIB_UTILITY_Hh_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/*
|
||||
This header contains back-ports of C++14/17 functions and type traits
|
||||
found in <utility> header of the standard library.
|
||||
*/
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
template<std::size_t... Ints>
|
||||
struct index_sequence
|
||||
{
|
||||
using type = index_sequence;
|
||||
using value_type = std::size_t;
|
||||
static constexpr std::size_t size() noexcept {return sizeof...(Ints);}
|
||||
};
|
||||
|
||||
template<class Sequence1, class Sequence2>
|
||||
struct merge_and_renumber;
|
||||
|
||||
template<std::size_t... I1, std::size_t... I2>
|
||||
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
|
||||
: index_sequence < I1..., (sizeof...(I1) + I2)... > {};
|
||||
|
||||
template<std::size_t N>
|
||||
struct make_index_sequence
|
||||
: merge_and_renumber < typename make_index_sequence < N / 2 >::type,
|
||||
typename make_index_sequence < N - N / 2 >::type > {};
|
||||
|
||||
template<> struct make_index_sequence<0> : index_sequence<> {};
|
||||
template<> struct make_index_sequence<1> : index_sequence<0> {};
|
||||
|
||||
template<typename... Ts>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
|
||||
}
|
||||
|
||||
#endif //DLIB_UTILITY_Hh_
|
Loading…
Reference in New Issue
Block a user