Skip to content
Snippets Groups Projects
Commit 1f210072 authored by Paul Gessinger's avatar Paul Gessinger
Browse files

Workaround for is_invokable problem

Uses detection idiom instead of is_invokable in template args for
SFINAE.
parent b70e4593
No related branches found
No related tags found
1 merge request!797Workaround for is_invokable problem
Pipeline #1505510 passed
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "Acts/Utilities/TypeTraits.hpp"
namespace Acts { namespace Acts {
namespace Test { namespace Test {
...@@ -348,45 +350,41 @@ struct MicroBenchmarkIterImpl<Callable, void, void> { ...@@ -348,45 +350,41 @@ struct MicroBenchmarkIterImpl<Callable, void, void> {
} }
}; };
// Trick to have a static_assert fire only when the base case of a template template <typename T, typename I>
// specialization chain is instantiated using call_with_input_t = decltype(std::declval<T>()(std::declval<I>()));
template <typename T>
constexpr bool always_false = false;
// Mechanism to type-check that Callable has the right signature, avoiding
// crazy compiler errors deep down in the implementation.
template <typename Callable, typename Input = void, typename Enable = void>
struct MicroBenchmarkIter {
// Hitting the base case is an error
static_assert(always_false<Callable>,
"Bad benchmark iteration function signature");
// Still provide a method with expected signature so that the above template <typename T>
// static_assert error is the only compiler error that gets emitted. using call_without_input_t = decltype(std::declval<T>()());
static inline void iter(const Callable&, const Input* = nullptr) {}
};
// If callable is a callable that takes the expected input argument type, then // If callable is a callable that takes the expected input argument type, then
// this specialization will be selected... // this specialization will be selected...
template <typename Callable, typename Input> template <typename Callable, typename Input = void>
struct MicroBenchmarkIter< struct MicroBenchmarkIter {
Callable, Input, std::enable_if_t<std::is_invocable_v<Callable, Input>>> { constexpr static bool is_callable =
// ...so we can safely call invoke_result_t here concept ::exists<call_with_input_t, Callable, Input>;
static inline void iter(const Callable& iteration, const Input* input) { static inline void iter(const Callable& iteration, const Input* input) {
using Result = std::invoke_result_t<Callable, const Input&>; static_assert(is_callable,
MicroBenchmarkIterImpl<Callable, Input, Result>::iter(iteration, *input); "Gave callable that is not callable without input");
if constexpr (is_callable) {
using Result = std::invoke_result_t<Callable, const Input&>;
MicroBenchmarkIterImpl<Callable, Input, Result>::iter(iteration, *input);
}
} }
}; };
// If Callable is a callable that takes no argument, this specialization will be // If Callable is a callable that takes no argument, this specialization will be
// picked instead of the one above... // picked instead of the one above...
template <typename Callable> template <typename Callable>
struct MicroBenchmarkIter<Callable, void, struct MicroBenchmarkIter<Callable, void> {
std::enable_if_t<std::is_invocable_v<Callable>>> { constexpr static bool is_callable =
// ...so we can safely call invoke_result_t here concept ::exists<call_without_input_t, Callable>;
static inline void iter(const Callable& iteration, const void* = nullptr) { static inline void iter(const Callable& iteration, const void* = nullptr) {
using Result = std::invoke_result_t<Callable>; static_assert(is_callable, "Gave callable that is not callable with input");
MicroBenchmarkIterImpl<Callable, void, Result>::iter(iteration); if constexpr (is_callable) {
using Result = std::invoke_result_t<Callable>;
MicroBenchmarkIterImpl<Callable, void, Result>::iter(iteration);
}
} }
}; };
...@@ -472,6 +470,7 @@ MicroBenchmarkResult microBenchmarkImpl(Callable&& run, size_t iters_per_run, ...@@ -472,6 +470,7 @@ MicroBenchmarkResult microBenchmarkImpl(Callable&& run, size_t iters_per_run,
// (which is a good approximation of the elapsed time). // (which is a good approximation of the elapsed time).
// * Note after how much elapsed time the timings typically become steady. // * Note after how much elapsed time the timings typically become steady.
// //
template <typename Callable> template <typename Callable>
MicroBenchmarkResult microBenchmark( MicroBenchmarkResult microBenchmark(
Callable&& iteration, size_t iters_per_run, size_t num_runs = 20000, Callable&& iteration, size_t iters_per_run, size_t num_runs = 20000,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment