From: Luca Heltai Date: Fri, 9 Aug 2019 04:25:27 +0000 (-0600) Subject: Mutable bind implementation. X-Git-Tag: v9.2.0-rc1~1175^2 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2334d8bec5bd236b37b88337f85f845ac8698d2;p=dealii.git Mutable bind implementation. --- diff --git a/doc/news/changes/minor/20190808LucaHeltaiMatthiasMaier b/doc/news/changes/minor/20190808LucaHeltaiMatthiasMaier new file mode 100644 index 0000000000..6b5da92a53 --- /dev/null +++ b/doc/news/changes/minor/20190808LucaHeltaiMatthiasMaier @@ -0,0 +1,3 @@ +New: Added MutableBind class and mutable_bind() method. +
+(Luca Heltai, Matthias Maier, 2019/08/08) diff --git a/include/deal.II/base/mutable_bind.h b/include/deal.II/base/mutable_bind.h new file mode 100644 index 0000000000..eb0824d4a7 --- /dev/null +++ b/include/deal.II/base/mutable_bind.h @@ -0,0 +1,334 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2019 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_base_mutable_bind_h +#define dealii_base_mutable_bind_h + +#include + +#include +#include +#include + +#include + +DEAL_II_NAMESPACE_OPEN + +namespace Utilities +{ + /** + * A mutable version of std::bind, that binds all arguments of a function + * pointer to a stored tuple, and allows you to update the tuple between + * calls. + * + * An example usage of this class is through the helper function + * mutable_bind() that creates a MutableBind object on the fly, based on its + * arguments: + * + * @code + * void my_function(const int &a, const double &b); + * + * auto bound = mutable_bind(my_function, 1, 2.0); + * + * bound(); // will execute my_function(1, 2.0); + * + * bound.set_arguments(2, 3.0); + * bound(); // will execute my_function(2, 3.0); + * + * bound.parse_arguments("3: 4.0"); + * bound(); // will execute my_function(3, 4.0); + * @endcode + * + * The arguments are copied to the tuple, with their reference and const + * attributes removed. Only copy constructible objects are allowed as + * function arguments. If you need to keep some references around, you may + * wrap your function into a std::bind object: + * + * @code + * void + * example_function(const Point<2> &p, + * const double &d, + * const unsigned int i = 3) { + * ... + * }; + * + * const Point<2> p(1, 2); + * + * Utilities::MutableBind exp = { + * std::bind(example_function, + * std::cref(p), + * std::placeholders::_1, + * std::placeholders::_2), + * {}}; + * + * exp.parse_arguments("3.0 : 4"); + * exp(); // calls example_function(p, 3.0, 4); + * @endcode + * + * @authors Luca Heltai, Matthias Maier, 2019. + */ + template + class MutableBind + { + public: + /** + * An alias to the stored std::tuple type. Only copy constructible + * objects are allowed as tuple members. + */ + using TupleType = std::tuple::type>::type...>; + + /** + * Construct a MutableBind object specifying the function, and + * each arguments separately. + */ + template + MutableBind(FunctionType function, FunctionArgs &&... arguments); + + /** + * Construct a MutableBind object specifying the function, and + * the arguments as a tuple. + */ + template + MutableBind(FunctionType function, TupleType &&arguments); + + /** + * Construct a MutableBind object specifying only the function. By default, + * the arguments are left to their defult constructor values. + */ + template + MutableBind(FunctionType function); + + /** + * Call the original function, passing as arguments the elements of the + * tuple of bound arguments. + */ + ReturnType + operator()() const; + + /** + * Set the arguments to use in @p function, for next time + * operator()() is called, using move semantic. + */ + void + set_arguments(TupleType &&arguments); + + /** + * Set the arguments to use in @p function, for next time + * operator()() is called, using move semantic. + */ + void + set_arguments(FunctionArgs &&... arguments); + + /** + * Parse the arguments to use in @p function from a string, for next time + * operator()() is called. + * + * The conversion is performed using a user supplied Patterns::PatternBase + * object. By default, Patterns::Tools::Convert::to_pattern() is + * used to determine how to convert from @p value_string to a TupleType + * object. + * + * @param value_string The string to convert from + * @param pattern A unique pointer to the pattern to use when performing + * the conversion + */ + void + parse_arguments(const std::string & value_string, + const std::unique_ptr &pattern = + Patterns::Tools::Convert::to_pattern()); + + private: + /** + * An std::function that stores the original function. + */ + const std::function function; + + /** + * Currently stored arguments. These are forwarded to the function object + * above, when calling operator()(). + */ + TupleType arguments; + }; + + + + /** + * Create a MutableBind object from a function pointer and a list of + * arguments. + * + * An example usage is given by: + * @code + * void my_function(const int &a, const double &b); + * + * auto bound = mutable_bind(my_function, 1, 2.0); + * + * bound(); // will execute my_function(1, 2.0); + * + * bound.set_arguments(2, 3.0); + * bound(); // will execute my_function(2, 3.0); + * + * bound.parse_arguments("3: 4.0"); + * bound(); // will execute my_function(3, 4.0); + * @endcode + * + * @authors Luca Heltai, Matthias Maier, 2019. + */ + template + MutableBind + mutable_bind(ReturnType (*function)(FunctionArgs...), + typename identity::type &&... arguments); + + /** + * Same as above, using a std::function object. + */ + template + MutableBind + mutable_bind(std::function, + typename identity::type &&... arguments); + + /** + * Create a MutableBind object from a function pointer, with uninitialized + * arguments. + * + * Notice that if you do not call one of the MutableBind::set_arguments() + * methods, or the MutableBind::parse_arguments() function on the returned + * object, then the arguments passed to the function object will be + * initialized with the values coming from each of the arguments' default + * constructors. + */ + template + MutableBind + mutable_bind(ReturnType (*function)(FunctionArgs...)); + + /** + * Same as above, using a std::function object. + */ + template + MutableBind + mutable_bind(std::function); + + + +#ifndef DOXYGEN + template + template + MutableBind::MutableBind( + FunctionType function, + FunctionArgs &&... arguments) + : function(function) + , arguments(std::make_tuple(std::move(arguments)...)){}; + + + + template + template + MutableBind::MutableBind(FunctionType function, + TupleType && arguments) + : function(function) + , arguments(std::move(arguments)){}; + + + + template + template + MutableBind::MutableBind(FunctionType function) + : function(function){}; + + + + template + ReturnType + MutableBind::operator()() const + { + return std_cxx17::apply(function, arguments); + } + + + + template + void + MutableBind::set_arguments( + FunctionArgs &&... args) + { + arguments = std::make_tuple(std::move(args)...); + } + + + + template + void + MutableBind::set_arguments(TupleType &&args) + { + arguments = std::move(args); + } + + + + template + void + MutableBind::parse_arguments( + const std::string & value_string, + const std::unique_ptr &pattern) + { + arguments = + Patterns::Tools::Convert::to_value(value_string, pattern); + } + + + + template + MutableBind + mutable_bind(ReturnType (*function)(FunctionArgs...), + typename identity::type &&... arguments) + { + return MutableBind(function, + std::move(arguments)...); + }; + + + + template + MutableBind + mutable_bind(ReturnType (*function)(FunctionArgs...)) + { + return MutableBind(function); + } + + + + template + MutableBind + mutable_bind(std::function function, + typename identity::type &&... arguments) + { + return MutableBind(function, + std::move(arguments)...); + } + + + + template + MutableBind + mutable_bind(std::function function) + { + return MutableBind(function); + } +#endif +} // namespace Utilities + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/tests/base/mutable_bind_01.cc b/tests/base/mutable_bind_01.cc new file mode 100644 index 0000000000..1c532a2803 --- /dev/null +++ b/tests/base/mutable_bind_01.cc @@ -0,0 +1,51 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2019 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. +// +// --------------------------------------------------------------------- + +// test the implementation of the Utilities::mutable_bind function + + +#include +#include +#include + +#include "../tests.h" + +void +example_function(const Point<2> &p, const double &d, const unsigned int i = 3) +{ + deallog << "P: " << p << ", d: " << d << ", i: " << i << std::endl; +} + +int +main() +{ + initlog(); + + auto exp = Utilities::mutable_bind(example_function); + exp(); + + exp.set_arguments(std::make_tuple(Point<2>(.5, .5), 2.0, 3)); + exp(); + + exp.parse_arguments("2.0 , 2.0 : 1.0 : 4"); + exp(); + + exp.set_arguments(Point<2>(.3, .3), 2.0, 5); + exp(); + + auto exp1 = + Utilities::mutable_bind(example_function, Point<2>(.8, .8), 4.0, 10); + exp1(); +} diff --git a/tests/base/mutable_bind_01.output b/tests/base/mutable_bind_01.output new file mode 100644 index 0000000000..3df373e329 --- /dev/null +++ b/tests/base/mutable_bind_01.output @@ -0,0 +1,6 @@ + +DEAL::P: 0.00000 0.00000, d: 0.00000, i: 0 +DEAL::P: 0.500000 0.500000, d: 2.00000, i: 3 +DEAL::P: 2.00000 2.00000, d: 1.00000, i: 4 +DEAL::P: 0.300000 0.300000, d: 2.00000, i: 5 +DEAL::P: 0.800000 0.800000, d: 4.00000, i: 10 diff --git a/tests/base/mutable_bind_02.cc b/tests/base/mutable_bind_02.cc new file mode 100644 index 0000000000..d80e08cfc8 --- /dev/null +++ b/tests/base/mutable_bind_02.cc @@ -0,0 +1,55 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2019 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. +// +// --------------------------------------------------------------------- + +// test the implementation of the Utilities::mutable_bind function. Make +// sure it works for std::function objects. + +#include +#include +#include + +#include "../tests.h" + +void +example_function(const Point<2> &p, const double &d, const unsigned int i = 3) +{ + deallog << "P: " << p << ", d: " << d << ", i: " << i << std::endl; +} + +int +main() +{ + initlog(); + + Point<2> p(.1, .1); + + std::function reduced_function = + [&p](const double d, const unsigned int i) { example_function(p, d, i); }; + + auto exp = Utilities::mutable_bind(reduced_function); + exp(); + + exp.set_arguments(std::make_tuple(2.0, 3)); + exp(); + + exp.parse_arguments("1.0 : 4"); + exp(); + + exp.set_arguments(2.0, 5); + exp(); + + auto exp1 = Utilities::mutable_bind(reduced_function, 4.0, 10); + exp1(); +} diff --git a/tests/base/mutable_bind_02.output b/tests/base/mutable_bind_02.output new file mode 100644 index 0000000000..5d55d3f9d2 --- /dev/null +++ b/tests/base/mutable_bind_02.output @@ -0,0 +1,6 @@ + +DEAL::P: 0.100000 0.100000, d: 0.00000, i: 0 +DEAL::P: 0.100000 0.100000, d: 2.00000, i: 3 +DEAL::P: 0.100000 0.100000, d: 1.00000, i: 4 +DEAL::P: 0.100000 0.100000, d: 2.00000, i: 5 +DEAL::P: 0.100000 0.100000, d: 4.00000, i: 10 diff --git a/tests/base/mutable_bind_03.cc b/tests/base/mutable_bind_03.cc new file mode 100644 index 0000000000..4fd542a131 --- /dev/null +++ b/tests/base/mutable_bind_03.cc @@ -0,0 +1,49 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2019 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. +// +// --------------------------------------------------------------------- + +// test the implementation of the Utilities::mutable_bind function with +// std::bind objects + + +#include +#include +#include + +#include "../tests.h" + +void +example_function(const Point<2> &p, const double &d, const unsigned int i = 3) +{ + deallog << "P: " << p << ", d: " << d << ", i: " << i << std::endl; +} + +int +main() +{ + initlog(1); + + const Point<2> p(1, 2); + + Utilities::MutableBind exp = { + std::bind(example_function, + std::cref(p), + std::placeholders::_1, + std::placeholders::_2), + {}}; + + exp(); + exp.set_arguments(3.0, 4); + exp(); +} diff --git a/tests/base/mutable_bind_03.output b/tests/base/mutable_bind_03.output new file mode 100644 index 0000000000..9ff7b20815 --- /dev/null +++ b/tests/base/mutable_bind_03.output @@ -0,0 +1,3 @@ + +DEAL::P: 1.00000 2.00000, d: 0.00000, i: 0 +DEAL::P: 1.00000 2.00000, d: 3.00000, i: 4 diff --git a/tests/base/mutable_bind_04.cc b/tests/base/mutable_bind_04.cc new file mode 100644 index 0000000000..d0aea62937 --- /dev/null +++ b/tests/base/mutable_bind_04.cc @@ -0,0 +1,56 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2019 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. +// +// --------------------------------------------------------------------- + +// test the implementation of the Utilities::mutable_bind function with +// std::bind objects and std::cref + + +#include +#include +#include + +#include "../tests.h" + +void +example_function(const Point<2> &p, const double &d, const unsigned int i = 3) +{ + deallog << "P: " << p << ", d: " << d << ", i: " << i << std::endl; +} + +int +main() +{ + initlog(1); + + const Point<2> p(1, 2); + const Point<2> p2(2, 3); + + Utilities::MutableBind>, + double, + unsigned int> + exp(std::bind(example_function, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + std::cref(p), + 3.0, + 4); + + exp.set_arguments(std::cref(p), 3.0, 4); + exp(); // executes example_function(p, 3.0, 4); + exp.set_arguments(std::cref(p2), 4.0, 5); + exp(); // executes example_function(p2, 4.0, 5); +} diff --git a/tests/base/mutable_bind_04.output b/tests/base/mutable_bind_04.output new file mode 100644 index 0000000000..3bab5965d0 --- /dev/null +++ b/tests/base/mutable_bind_04.output @@ -0,0 +1,3 @@ + +DEAL::P: 1.00000 2.00000, d: 3.00000, i: 4 +DEAL::P: 2.00000 3.00000, d: 4.00000, i: 5