mirror of
https://github.com/boostorg/core.git
synced 2025-07-30 21:07:22 +02:00
@ -1,12 +1,19 @@
|
|||||||
[/
|
[/
|
||||||
Copyright 2021 Peter Dimov
|
Copyright 2021 Peter Dimov
|
||||||
Copyright 2022-2023 Andrey Semashev
|
Copyright 2022-2024 Andrey Semashev
|
||||||
Distributed under the Boost Software License, Version 1.0.
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
https://boost.org/LICENSE_1_0.txt)
|
https://boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section Revision History]
|
[section Revision History]
|
||||||
|
|
||||||
|
[section Changes in 1.85.0]
|
||||||
|
|
||||||
|
* Added a new [link core.functor `boost/core/functor.hpp`] header with a `functor` class template
|
||||||
|
for wrapping a raw function into a function object class.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
[section Changes in 1.84.0]
|
[section Changes in 1.84.0]
|
||||||
|
|
||||||
* `boost::swap` utility function has been renamed to `boost::core::invoke_swap` to
|
* `boost::swap` utility function has been renamed to `boost::core::invoke_swap` to
|
||||||
|
@ -55,6 +55,7 @@ criteria for inclusion is that the utility component be:
|
|||||||
[include exchange.qbk]
|
[include exchange.qbk]
|
||||||
[include explicit_operator_bool.qbk]
|
[include explicit_operator_bool.qbk]
|
||||||
[include first_scalar.qbk]
|
[include first_scalar.qbk]
|
||||||
|
[include functor.qbk]
|
||||||
[include identity.qbk]
|
[include identity.qbk]
|
||||||
[include ignore_unused.qbk]
|
[include ignore_unused.qbk]
|
||||||
[include is_same.qbk]
|
[include is_same.qbk]
|
||||||
|
101
doc/functor.qbk
Normal file
101
doc/functor.qbk
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
[/
|
||||||
|
/ Copyright (c) 2024 Andrey Semashev
|
||||||
|
/
|
||||||
|
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
/]
|
||||||
|
|
||||||
|
[section:functor functor]
|
||||||
|
|
||||||
|
[simplesect Authors]
|
||||||
|
|
||||||
|
* Andrey Semashev
|
||||||
|
|
||||||
|
[endsimplesect]
|
||||||
|
|
||||||
|
[section Header <boost/core/functor.hpp>]
|
||||||
|
|
||||||
|
[note This component requires a compiler supporting C++17 or newer.]
|
||||||
|
|
||||||
|
The header `<boost/core/functor.hpp>` defines the `boost::core::functor` class template
|
||||||
|
that wraps a raw function specified in its template parameter into a function object class.
|
||||||
|
The function object forwards any arguments passed to it to the wrapped function and returns
|
||||||
|
the result of the call.
|
||||||
|
|
||||||
|
The `functor` wrapper can be useful in cases when a function object class type is required,
|
||||||
|
for example, for use with smart pointers such as `std::unique_ptr`, where the actual logic
|
||||||
|
of the function object is already implemented as a raw function, possibly provided by a
|
||||||
|
third party library. Since `functor` is default-constructible and does not store a pointer
|
||||||
|
to the wrapped function internally, using `functor` is less error-prone and more efficient
|
||||||
|
than using the pointer to function instead. For example, with `std::unique_ptr` you don't
|
||||||
|
need to pass a pointer to the deleter function in the `std::unique_ptr` constructor, and
|
||||||
|
the `std::unique_ptr` object does not store and invoke a pointer to the deleter function.
|
||||||
|
With `functor`, the deleter function becomes part of the `std::unique_ptr` type, which
|
||||||
|
prevents mixing pointers with incompatible deleters.
|
||||||
|
|
||||||
|
```
|
||||||
|
void my_deleter(void* p);
|
||||||
|
|
||||||
|
using malloc_ptr = std::unique_ptr< char, boost::core::functor< std::free > >;
|
||||||
|
using my_ptr = std::unique_ptr< char, boost::core::functor< my_deleter > >;
|
||||||
|
|
||||||
|
my_ptr create_string(std::size_t size);
|
||||||
|
void consume_string(my_ptr&& str);
|
||||||
|
|
||||||
|
malloc_ptr ptr1(static_cast< char* >(std::malloc(size)));
|
||||||
|
// ptr1 = allocate_string(size); // error, cannot convert my_ptr to malloc_ptr
|
||||||
|
my_ptr ptr2 = create_string(size); // ok
|
||||||
|
|
||||||
|
// consume_string(std::move(ptr1)); // error, cannot convert malloc_ptr&& to my_ptr
|
||||||
|
consume_string(std::move(ptr2)); // ok
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `functor` may also be beneficial for reducing generated code sizes. For example, in
|
||||||
|
order to avoid storing and invoking a pointer to the deleter function in `std::shared_ptr`,
|
||||||
|
one may be inclined to use lambda functions to wrap the deleter function call like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
std::shared_ptr< int > ptr(static_cast< int* >(std::malloc(sizeof(int))), [](int* p) { std::free(p); });
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem is that every lambda function declaration introduces a unique type, even if
|
||||||
|
the lambda function definition matches exactly one of the previously declared lambda
|
||||||
|
functions. Thus, if `std::shared_ptr` objects like the one above are created in multiple
|
||||||
|
places in the program, the definition of the shared pointer counter and associated code
|
||||||
|
and data (e.g. virtual function table) will be duplicated for each instance.
|
||||||
|
|
||||||
|
Replacing the lambda function with `functor` solves this problem without sacrificing
|
||||||
|
readability or efficiency:
|
||||||
|
|
||||||
|
```
|
||||||
|
std::shared_ptr< int > ptr(static_cast< int* >(std::malloc(sizeof(int))), boost::core::functor< std::free >());
|
||||||
|
```
|
||||||
|
|
||||||
|
[section Synopsis]
|
||||||
|
|
||||||
|
```
|
||||||
|
namespace boost::core {
|
||||||
|
|
||||||
|
template< auto Function >
|
||||||
|
struct functor
|
||||||
|
{
|
||||||
|
template< typename... Args >
|
||||||
|
decltype(auto) operator() (Args&&... args) const noexcept(...);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boost::core
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[section `template< typename... Args > decltype(auto) operator() (Args&&... args) const noexcept(...);`]
|
||||||
|
|
||||||
|
* *Effects:* `return Function(std::forward< Args >(args)...)`.
|
||||||
|
* *Throws:* Nothing, unless invoking `Function` throws.
|
||||||
|
* *Note:* This function only participates in overload resolution if `Function(std::forward< Args >(args)...)` is a valid call expression.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[endsect]
|
34
include/boost/core/functor.hpp
Normal file
34
include/boost/core/functor.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright Andrey Semashev 2024.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* \file functor.hpp
|
||||||
|
* \author Andrey Semashev
|
||||||
|
* \date 2024-01-23
|
||||||
|
*
|
||||||
|
* This header contains a \c functor implementation. This is a function object
|
||||||
|
* that invokes a function that is specified as its template parameter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOOST_CORE_FUNCTOR_HPP
|
||||||
|
#define BOOST_CORE_FUNCTOR_HPP
|
||||||
|
|
||||||
|
namespace boost::core {
|
||||||
|
|
||||||
|
//! A function object that invokes a function specified as its template parameter
|
||||||
|
template< auto Function >
|
||||||
|
struct functor
|
||||||
|
{
|
||||||
|
template< typename... Args >
|
||||||
|
auto operator() (Args&&... args) const noexcept(noexcept(Function(static_cast< Args&& >(args)...))) -> decltype(Function(static_cast< Args&& >(args)...))
|
||||||
|
{
|
||||||
|
return Function(static_cast< Args&& >(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boost::core
|
||||||
|
|
||||||
|
#endif // BOOST_CORE_FUNCTOR_HPP
|
@ -189,6 +189,8 @@ run underlying_type.cpp ;
|
|||||||
|
|
||||||
run fclose_deleter_test.cpp : : : <target-os>windows:<define>_CRT_SECURE_NO_WARNINGS <target-os>windows:<define>_CRT_SECURE_NO_DEPRECATE ;
|
run fclose_deleter_test.cpp : : : <target-os>windows:<define>_CRT_SECURE_NO_WARNINGS <target-os>windows:<define>_CRT_SECURE_NO_DEPRECATE ;
|
||||||
|
|
||||||
|
run functor_test.cpp ;
|
||||||
|
|
||||||
run pointer_traits_pointer_test.cpp ;
|
run pointer_traits_pointer_test.cpp ;
|
||||||
run pointer_traits_element_type_test.cpp ;
|
run pointer_traits_element_type_test.cpp ;
|
||||||
run pointer_traits_difference_type_test.cpp ;
|
run pointer_traits_difference_type_test.cpp ;
|
||||||
|
202
test/functor_test.cpp
Normal file
202
test/functor_test.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright Andrey Semashev 2024.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* \file functor_test.cpp
|
||||||
|
* \author Andrey Semashev
|
||||||
|
* \date 2024-01-23
|
||||||
|
*
|
||||||
|
* This file contains tests for \c boost::core::functor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <boost/config.hpp>
|
||||||
|
|
||||||
|
#if !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
||||||
|
|
||||||
|
#include <boost/core/functor.hpp>
|
||||||
|
#include <boost/core/lightweight_test.hpp>
|
||||||
|
#include <boost/core/lightweight_test_trait.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#if (defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703l)) || \
|
||||||
|
(defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (BOOST_CXX_VERSION >= 201703l))
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
using std::is_invocable;
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
// A simplified implementation that does not support member function pointers
|
||||||
|
template< typename Func, typename... Args >
|
||||||
|
struct is_invocable_impl
|
||||||
|
{
|
||||||
|
template< typename F = Func, typename = decltype(std::declval< F >()(std::declval< Args >()...)) >
|
||||||
|
static std::true_type _check_invocable(int);
|
||||||
|
static std::false_type _check_invocable(...);
|
||||||
|
|
||||||
|
typedef decltype(is_invocable_impl::_check_invocable(0)) type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename Func, typename... Args >
|
||||||
|
struct is_invocable : public is_invocable_impl< Func, Args... >::type { };
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int g_n = 0;
|
||||||
|
|
||||||
|
void void_func()
|
||||||
|
{
|
||||||
|
++g_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int int_func()
|
||||||
|
{
|
||||||
|
return ++g_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int& int_ref_func()
|
||||||
|
{
|
||||||
|
++g_n;
|
||||||
|
return g_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void void_add1(int x)
|
||||||
|
{
|
||||||
|
g_n += x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add2(int x, int y)
|
||||||
|
{
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_ns {
|
||||||
|
|
||||||
|
int add3(int x, int y, int z)
|
||||||
|
{
|
||||||
|
return x + y + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_ns
|
||||||
|
|
||||||
|
int int_func_noexcept() noexcept
|
||||||
|
{
|
||||||
|
return ++g_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
boost::core::functor< void_func > fun;
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_func >& >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_func > const& >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< void_func > const&, int >));
|
||||||
|
BOOST_TEST_EQ(noexcept(fun()), false);
|
||||||
|
fun();
|
||||||
|
BOOST_TEST_EQ(g_n, 1);
|
||||||
|
fun();
|
||||||
|
BOOST_TEST_EQ(g_n, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_n = 0;
|
||||||
|
{
|
||||||
|
boost::core::functor< int_func > fun;
|
||||||
|
int res = fun();
|
||||||
|
BOOST_TEST_EQ(res, 1);
|
||||||
|
BOOST_TEST_EQ(g_n, 1);
|
||||||
|
res = fun();
|
||||||
|
BOOST_TEST_EQ(res, 2);
|
||||||
|
BOOST_TEST_EQ(g_n, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_n = 0;
|
||||||
|
{
|
||||||
|
boost::core::functor< int_ref_func > fun;
|
||||||
|
int& res1 = fun();
|
||||||
|
BOOST_TEST_EQ(&res1, &g_n);
|
||||||
|
BOOST_TEST_EQ(res1, 1);
|
||||||
|
int& res2 = fun();
|
||||||
|
BOOST_TEST_EQ(&res2, &g_n);
|
||||||
|
BOOST_TEST_EQ(res2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_n = 0;
|
||||||
|
{
|
||||||
|
boost::core::functor< void_add1 > fun;
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< void_add1 >& >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< void_add1 > const& >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_add1 >&, int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_add1 > const&, int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_add1 >&, short int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< void_add1 > const&, short int >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< void_add1 > const&, int, int >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< void_add1 > const&, const char* >));
|
||||||
|
fun(10);
|
||||||
|
BOOST_TEST_EQ(g_n, 10);
|
||||||
|
fun(20);
|
||||||
|
BOOST_TEST_EQ(g_n, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::core::functor< add2 > fun;
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 >& >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 > const& >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 >&, int >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 > const&, int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< add2 >&, int, int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< add2 > const&, int, int >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< add2 >&, short int, signed char >));
|
||||||
|
BOOST_TEST_TRAIT_TRUE((test::is_invocable< boost::core::functor< add2 > const&, short int, signed char >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 > const&, const char* >));
|
||||||
|
BOOST_TEST_TRAIT_FALSE((test::is_invocable< boost::core::functor< add2 > const&, const char*, float >));
|
||||||
|
int res = fun(10, 20);
|
||||||
|
BOOST_TEST_EQ(res, 30);
|
||||||
|
res = fun(30, 40);
|
||||||
|
BOOST_TEST_EQ(res, 70);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::core::functor< test_ns::add3 > fun;
|
||||||
|
int res = fun(10, 20, 30);
|
||||||
|
BOOST_TEST_EQ(res, 60);
|
||||||
|
res = fun(40, 50, 60);
|
||||||
|
BOOST_TEST_EQ(res, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_n = 0;
|
||||||
|
{
|
||||||
|
boost::core::functor< int_func_noexcept > fun;
|
||||||
|
BOOST_TEST_EQ(noexcept(fun()), true);
|
||||||
|
int res = fun();
|
||||||
|
BOOST_TEST_EQ(res, 1);
|
||||||
|
BOOST_TEST_EQ(g_n, 1);
|
||||||
|
res = fun();
|
||||||
|
BOOST_TEST_EQ(res, 2);
|
||||||
|
BOOST_TEST_EQ(g_n, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::report_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
||||||
|
|
||||||
|
#include <boost/config/pragma_message.hpp>
|
||||||
|
|
||||||
|
BOOST_PRAGMA_MESSAGE("Test skipped because C++17 auto non-type template parameters are not supported")
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
Reference in New Issue
Block a user