diff --git a/dune/common/CMakeLists.txt b/dune/common/CMakeLists.txt index 5b63b16237c22e8d4f4677d398bb90839d7cf0b4..efcfbb6d061f8a84d8636730c8d2721787aba02c 100644 --- a/dune/common/CMakeLists.txt +++ b/dune/common/CMakeLists.txt @@ -74,6 +74,7 @@ install(FILES math.hh matvectraits.hh nullptr.hh + overloadset.hh parametertree.hh parametertreeparser.hh path.hh diff --git a/dune/common/overloadset.hh b/dune/common/overloadset.hh new file mode 100644 index 0000000000000000000000000000000000000000..4f0aaf663a78eb72cd9f0bf6d6ee191193c9cf45 --- /dev/null +++ b/dune/common/overloadset.hh @@ -0,0 +1,166 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_OVERLOADSET_HH +#define DUNE_COMMON_OVERLOADSET_HH + +#include <utility> +#include <type_traits> + +#include <dune/common/std/type_traits.hh> + + + +namespace Dune { + +namespace Impl { + + // This overload set derives from + // all passed functions. Since we + // cannot do argument pack expansion + // on using statements this is done recursively. + template<class F0, class... F> + class OverloadSet: public OverloadSet<F...>, F0 + { + using Base = OverloadSet<F...>; + public: + + template<class FF0, class... FF> + OverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward<FF>(ff)...), + F0(std::forward<FF0>(f0)) + {} + + using F0::operator(); + using Base::operator(); + }; + + template<class F0> + class OverloadSet<F0>: public F0 + { + public: + + template<class FF0> + OverloadSet(FF0&& f0) : + F0(std::forward<FF0>(f0)) + {} + + using F0::operator(); + }; + +} // end namespace Impl + + + +/** + * \brief Create an overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * The returned object derives from + * those implementations such that it contains + * all operator() implementations in its + * overload set. When calling operator() + * this will select the best overload. + * If multiple overload are equally good this + * will lead to ambiguity. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + */ +template<class... F> +auto overload(F&&... f) +{ + return Impl::OverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); +} + + + +namespace Impl { + + template<class F0, class... F> + class OrderedOverloadSet: public OrderedOverloadSet<F...>, F0 + { + using Base = OrderedOverloadSet<F...>; + public: + + template<class FF0, class... FF> + OrderedOverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward<FF>(ff)...), + F0(std::forward<FF0>(f0)) + {} + + template<class... Args, + std::enable_if_t<Std::is_callable<F0(Args&&...)>::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return F0::operator()(std::forward<Args>(args)...); + } + + template<class... Args, + std::enable_if_t< not Std::is_callable<F0(Args&&...)>::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return Base::operator()(std::forward<Args>(args)...); + } + + }; + + template<class F0> + class OrderedOverloadSet<F0>: public F0 + { + public: + + template<class FF0> + OrderedOverloadSet(FF0&& f0) : + F0(std::forward<FF0>(f0)) + {} + + template<class... Args> + decltype(auto) operator()(Args&&... args) + { + static_assert(Std::is_callable<F0(Args&&...)>::value, "No matching overload found in OrderedOverloadSet"); + return F0::operator()(std::forward<Args>(args)...); + } + }; + +} // end namespace Impl + + + +/** + * \brief Create an ordered overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * In contrast to overload() these overloads + * are ordered in the sense that the first + * matching overload for the given arguments + * is selected and later ones are ignored. + * Hence such a call is never ambiguous. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + */ +template<class... F> +auto orderedOverload(F&&... f) +{ + return Impl::OrderedOverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); +} + + + +} // end namespace Dune + +#endif // DUNE_COMMON_OVERLOADSET_HH diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt index 76907985923cc8817d32c77674333acf05caa022..26115f4d003324ae869c01d771e2119392f55e07 100644 --- a/dune/common/test/CMakeLists.txt +++ b/dune/common/test/CMakeLists.txt @@ -131,6 +131,9 @@ dune_add_test(NAME mpihelpertest2 LINK_LIBRARIES dunecommon ) +dune_add_test(SOURCES overloadsettest.cc + LINK_LIBRARIES dunecommon) + dune_add_test(SOURCES parametertreelocaletest.cc LINK_LIBRARIES dunecommon) diff --git a/dune/common/test/overloadsettest.cc b/dune/common/test/overloadsettest.cc new file mode 100644 index 0000000000000000000000000000000000000000..52c2935ab0f446fed7612e143a4508d1ce4fcdbe --- /dev/null +++ b/dune/common/test/overloadsettest.cc @@ -0,0 +1,97 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <tuple> + +#include <dune/common/overloadset.hh> +#include <dune/common/hybridutilities.hh> + +#include <dune/common/test/testsuite.hh> + + +int main() +{ + Dune::TestSuite test; + + { + auto foo = Dune::overload( + [](double i) { return 0; }, + [](int i) { return 1; }, + [](long i) { return 2; }); + + test.check(foo(3.14) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(long(42)) == 2) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::orderedOverload( + [](double i) { return 0; }, + [](int i) { return 1; }, + [](long i) { return 2; }); + + test.check(foo(3.14) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(long(42)) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::overload( + [](const int& i) { return 0; }, + [](int&& i) { return 1; }); + + int i = 0; + test.check(foo(long(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(i) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::orderedOverload( + [](const int& i) { return 0; }, + [](int&& i) { return 1; }); + + int i = 0; + test.check(foo(long(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(i) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto t = std::make_tuple(42, "foo", 3.14); + + auto typeToName = Dune::overload( + [](int) { return "int"; }, + [](long) { return "long"; }, + [](std::string) { return "string"; }, + [](float) { return "float"; }, + [](double) { return "double"; }); + + std::string tupleTypes; + Dune::Hybrid::forEach(t, [&](auto&& ti) { + tupleTypes += typeToName(ti); + }); + + test.check(tupleTypes == "intstringdouble") + << "traversal of tuple called incorrect overloads"; + } + + + return test.exit(); +}