-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15010 from sebproell/callable-member-function
Forward implementation of std::bind_front
- Loading branch information
Showing
3 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// --------------------------------------------------------------------- | ||
// | ||
// Copyright (C) 2023 by the deal.II authors | ||
// | ||
// This file is part of the deal.II library. | ||
// | ||
// The deal.II library is free software; you can use it, redistribute | ||
// it, and/or modify it under the terms of the GNU Lesser General | ||
// Public License as published by the Free Software Foundation; either | ||
// version 2.1 of the License, or (at your option) any later version. | ||
// The full text of the license can be found in the file LICENSE.md at | ||
// the top level directory of deal.II. | ||
// | ||
// --------------------------------------------------------------------- | ||
#ifndef dealii_cxx20_functional_h | ||
#define dealii_cxx20_functional_h | ||
|
||
#include <deal.II/base/config.h> | ||
|
||
#ifdef DEAL_II_HAVE_CXX20 | ||
# include <functional> | ||
#else | ||
# include <functional> | ||
# include <tuple> | ||
# include <utility> | ||
#endif | ||
|
||
DEAL_II_NAMESPACE_OPEN | ||
|
||
namespace std_cxx20 | ||
{ | ||
#ifndef DEAL_II_HAVE_CXX20 | ||
# ifndef DOXYGEN | ||
|
||
namespace internal | ||
{ | ||
/** | ||
* This call is used to invoke the lambda with stored bound arguments. | ||
* | ||
* @note The extra call is required to pattern-match the index_sequence. | ||
*/ | ||
template <typename F, | ||
size_t... Ind, | ||
typename BoundArgsTuple, | ||
typename... CallArgs> | ||
constexpr decltype(auto) | ||
call_bind(F &&function, | ||
std::index_sequence<Ind...>, | ||
BoundArgsTuple &&bound_args, | ||
CallArgs &&...call_args) | ||
{ | ||
return std::invoke(std::forward<F>(function), | ||
std::get<Ind>( | ||
std::forward<BoundArgsTuple>(bound_args))..., | ||
std::forward<CallArgs>(call_args)...); | ||
} | ||
|
||
/** | ||
* Return a callable which closely approximates what std::bind_front() is | ||
* doing. | ||
*/ | ||
template <typename F, typename... BoundArgs> | ||
decltype(auto) | ||
make_bind_front(F &&f, BoundArgs &&...bound_args) | ||
{ | ||
return [f = std::forward<F>(f), | ||
bound_args = std::make_tuple( | ||
std::forward<BoundArgs>(bound_args)...)](auto &&...call_args) { | ||
// Perform actual call inside a helper functions which allows to use | ||
// pattern-matching to the index sequence. | ||
return call_bind(f, | ||
std::index_sequence_for<BoundArgs...>{}, | ||
bound_args, | ||
std::forward<decltype(call_args)>(call_args)...); | ||
}; | ||
} | ||
|
||
} // namespace internal | ||
|
||
# endif | ||
|
||
/** | ||
* This function generates a forwarding call wrapper to the function @p f | ||
* which has its first `n` arguments bound to the `n` arguments passed in | ||
* @p bound_args. The implementation is an approximation to the functionality | ||
* provided by `std::bind_front`, see | ||
* https://en.cppreference.com/w/cpp/utility/functional/bind_front. | ||
* | ||
* This function allows to remove boilerplate in user code. Often functions in | ||
* the deal.II library take a `std::function` object as an argument. While you | ||
* can pass many different types of callable objects to a `std::function` | ||
* directly, you cannot pass a pointer to a member function. Instead you can | ||
* use the present function to create a compatible callable on-the-fly which | ||
* can be assigned to `std::function`. This is demonstrated in the following | ||
* example: | ||
* | ||
* @code | ||
* | ||
* // An exemplary function that takes an std::function as argument. | ||
* void example_function(const std::function<int(double)>& fn); | ||
* | ||
* class MyClass | ||
* { | ||
* public: | ||
* void do_something() | ||
* { | ||
* // Pass the member function | ||
* example_function( | ||
* std_cxx20::bind_front(&MyClass::my_function, this)); | ||
* } | ||
* | ||
* private: | ||
* // This function has a signature which is compatible with the function | ||
* // that is expected by example_function(). | ||
* int my_function(double); | ||
* }; | ||
* | ||
* @endcode | ||
*/ | ||
template <typename F, typename... BoundArgs> | ||
decltype(auto) | ||
bind_front(F &&f, BoundArgs &&...bound_args) | ||
{ | ||
return internal::make_bind_front(std::forward<F>(f), | ||
std::forward<BoundArgs>(bound_args)...); | ||
} | ||
#else | ||
using std::bind_front; | ||
#endif | ||
|
||
} // namespace std_cxx20 | ||
|
||
DEAL_II_NAMESPACE_CLOSE | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// --------------------------------------------------------------------- | ||
// | ||
// Copyright (C) 2023 by the deal.II authors | ||
// | ||
// This file is part of the deal.II library. | ||
// | ||
// The deal.II library is free software; you can use it, redistribute | ||
// it, and/or modify it under the terms of the GNU Lesser General | ||
// Public License as published by the Free Software Foundation; either | ||
// version 2.1 of the License, or (at your option) any later version. | ||
// The full text of the license can be found in the file LICENSE.md at | ||
// the top level directory of deal.II. | ||
// | ||
// --------------------------------------------------------------------- | ||
|
||
#include <deal.II/base/std_cxx20/functional.h> | ||
|
||
#include "../tests.h" | ||
|
||
|
||
using namespace dealii; | ||
|
||
namespace | ||
{ | ||
//! Helper type which is move-only. | ||
struct MoveOnly | ||
{ | ||
explicit MoveOnly(int i) | ||
: i{i} | ||
{} | ||
|
||
MoveOnly(const MoveOnly &) = delete; | ||
MoveOnly(MoveOnly &&) noexcept = default; | ||
|
||
MoveOnly & | ||
operator=(const MoveOnly &) = delete; | ||
MoveOnly & | ||
operator=(MoveOnly &&) = default; | ||
|
||
~MoveOnly() = default; | ||
|
||
int i; | ||
}; | ||
|
||
|
||
// use an interesting signature with move-only types to test that perfect | ||
// forwarding works correctly | ||
void | ||
evaluate(const std::function<int(double, const MoveOnly &, MoveOnly &&)> &fn) | ||
{ | ||
double d{0.0}; | ||
MoveOnly m1{1}; | ||
MoveOnly m2{2}; | ||
fn(d, m1, std::move(m2)); | ||
} | ||
|
||
//! Test class which creates callable member functions and passes them to | ||
//! evaluate() | ||
class Test | ||
{ | ||
public: | ||
void | ||
run() | ||
{ | ||
auto f = std_cxx20::bind_front(&Test::member, this); | ||
f(0.0, MoveOnly{1}, MoveOnly{2}); | ||
evaluate(std_cxx20::bind_front(&Test::member, this)); | ||
evaluate(std_cxx20::bind_front(&Test::const_member, this)); | ||
} | ||
|
||
void | ||
run_const() const | ||
{ | ||
evaluate(std_cxx20::bind_front(&Test::const_member, this)); | ||
} | ||
|
||
private: | ||
int | ||
member(double d, const MoveOnly &ref, const MoveOnly &mov) | ||
{ | ||
deallog << d << " " << ref.i << " " << mov.i << std::endl; | ||
return 0; | ||
} | ||
|
||
int | ||
const_member(double d, const MoveOnly &ref, MoveOnly &&mov) const | ||
{ | ||
deallog << d << " " << ref.i << " " << mov.i << std::endl; | ||
return 0; | ||
} | ||
}; | ||
|
||
|
||
void | ||
f(int a, double b) | ||
{ | ||
deallog << a << " " | ||
<< " " << b << std::endl; | ||
} | ||
|
||
void | ||
f2(int a, MoveOnly &&b) | ||
{ | ||
deallog << a << " " | ||
<< " " << b.i << std::endl; | ||
} | ||
|
||
} // namespace | ||
|
||
int | ||
main() | ||
{ | ||
initlog(); | ||
|
||
Test test{}; | ||
test.run(); | ||
test.run_const(); | ||
|
||
std_cxx20::bind_front(f)(1, 2.0); | ||
std_cxx20::bind_front(f2, 1)(MoveOnly{2}); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
DEAL::0.00000 1 2 | ||
DEAL::0.00000 1 2 | ||
DEAL::0.00000 1 2 | ||
DEAL::0.00000 1 2 | ||
DEAL::1 2.00000 | ||
DEAL::1 2 |