/* MICROBENCHMARK-ADAPTOR.hpp - helper to support microbenchmarks Copyright (C) 2023, Hermann Vosseler   **Lumiera** is free software; you can redistribute it and/or modify it   under the terms of the GNU General Public License as published by the   Free Software Foundation; either version 2 of the License, or (at your   option) any later version. See the file COPYING for further details. */ /** @file microbenchmark-adaptor.hpp ** Helpers and wrappers so simplify usage of \ref microbenchmark.hpp. ** Notably the benchmark functions expect the actual »test subject« as a ** function or lambda with signature `size_t(size_t)`. The argument will be ** the loop index and the result value will be added into a checksum, which ** also ensures that the optimiser can not unroll the benchmark loop. ** However, in practical use this strict requirement for the signature ** turned out as a nuisance; this header provides some automatic adaption ** - accept any numeric type as argument ** - accept any numeric type as checksum contribution (cast to `size_t`) ** - accept signature `void(void)` ** */ #ifndef LIB_TEST_MICROBENCHMARK_ADAPTOR_H #define LIB_TEST_MICROBENCHMARK_ADAPTOR_H #include "lib/meta/function.hpp" #include "lib/meta/util.hpp" namespace lib { namespace test{ namespace microbenchmark { using lib::meta::enable_if; using std::is_arithmetic; using std::is_same; using std::__and_; using std::__not_; /** * @internal helper to expose the signature `size_t(size_t)` * by wrapping a given lambda or functor. */ template struct Adaptor { static_assert (not sizeof(SIG), "Unable to adapt given functor."); }; template<> struct Adaptor { template static decltype(auto) wrap (FUN&& fun) { return std::forward(fun); } }; template<> struct Adaptor { template static auto wrap (FUN&& fun) { return [functor=std::forward(fun)] (size_t) mutable { functor(); return size_t(1); }; } }; template struct Adaptor, __not_> ,is_arithmetic, __not_> >>> { template static auto wrap (FUN&& fun) { return [functor=std::forward(fun)] (size_t i) mutable { return size_t(functor(i)); }; } }; template struct Adaptor, __not_> >>> { template static auto wrap (FUN&& fun) { return [functor=std::forward(fun)] (size_t) mutable { return size_t(functor()); }; } }; template struct Adaptor, __not_> >>> { template static auto wrap (FUN&& fun) { return [functor=std::forward(fun)] (size_t i) mutable { functor(i); return size_t(1); }; } }; /** * Adapter to expose the signature `size_t(size_t)` * from any suitable source functor or lambda * @note requirements * - arity must be either zero or one argument * - if argument or return type are present, * they must be plain arithmetic types * - no references allowed * - can be `void(void)` */ template inline decltype(auto) adapted4benchmark (FUN&& fun) { static_assert (lib::meta::_Fun(), "Need something function-like."); static_assert (lib::meta::_Fun::ARITY <=1, "Function with zero or one argument required."); using Sig = lib::meta::_Fun::Sig; return Adaptor::wrap (std::forward (fun)); } }}} // namespace lib::test::microbenchmark #endif /*LIB_TEST_MICROBENCHMARK_ADAPTOR_H*/