-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This type is intended as a sort-of replacement of `std::function` without the virtual function overhead of that type. This is ultimately meant to support the new extension mechanism for the KF and CKF, where these delegates will be used for the calibrator, smoother, and so on. Delegate type that allows type erasure of a callable without allocation and with a single level of indirection. This type can support: - a free function pointer - - a pointer to a member function alongside an instance pointer Note: `Delegate` does not assume ownership of the instance. You need to ensure that the lifetime of the callable instance is longer than that of the @c Delegate. Currently `Delegate` only supports callables that are `const`. The usage of this type looks like this for a free function: ```cpp int sumImplementation(int a, int b) { return a + b; } Delegate<int(int, int)> sum; sum.connect<&sumImplementation>(); int c = sum(2, 2); // = 4 ``` and like this for a member pointer with instance: ```cpp struct Subtractor { int x; int subtract(int v) const { return v - x; } }; Delegate<int(int)> subtract; Subtractor instance{5}; subtract.connect<&Subtractor::subtract>(&instance); int y = subtract(10); // = 5 ```
- Loading branch information
1 parent
2e48e5c
commit 2246d1a
Showing
3 changed files
with
248 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,120 @@ | ||
// This file is part of the Acts project. | ||
// | ||
// Copyright (C) 2021 CERN for the benefit of the Acts project | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
#pragma once | ||
|
||
#include <cassert> | ||
#include <functional> | ||
|
||
namespace Acts { | ||
|
||
template <typename> | ||
class Delegate; | ||
|
||
/// Delegate type that allows type erasure of a callable without allocation and | ||
/// with a single level of indirection. | ||
/// This type can support: | ||
/// - a free function pointer | ||
/// - a pointer to a member function alongside an instance pointer | ||
/// @note @c Delegate does not assume ownership of the instance. | ||
/// You need to ensure that the lifetime of the callable | ||
/// instance is longer than that of the @c Delegate. | ||
/// @note Currently @c Delegate only supports callables that are ``const`` | ||
/// @tparam R Return type of the function signature | ||
/// @tparam Args Types of the arguments of the function signatures | ||
/// | ||
template <typename R, typename... Args> | ||
class Delegate<R(Args...)> { | ||
/// Alias of the return type | ||
using return_type = R; | ||
/// Alias to the function pointer type this class will store | ||
using function_type = return_type (*)(const void*, Args...); | ||
|
||
public: | ||
Delegate() = default; | ||
|
||
/// Constructor with an explicit runtime callable | ||
/// @param callable The runtime value of the callable | ||
/// @note The function signature requires the first argument of the callable is `const void*`. | ||
/// i.e. if the signature of the delegate is `void(int)`, the callable's | ||
/// signature has to be `void(const void*, int)`. | ||
Delegate(function_type callable) { connect(callable); } | ||
|
||
/// Assignment operator with an explicit runtime callable | ||
/// @param callable The runtime value of the callable | ||
/// @note The function signature requires the first argument of the callable is `const void*`. | ||
/// i.e. if the signature of the delegate is `void(int)`, the callable's | ||
/// signature has to be `void(const void*, int)`. | ||
void operator=(function_type callable) { connect(callable); } | ||
|
||
/// Connect a free function pointer. | ||
/// @note The function pointer must be ``constexpr`` for @c Delegate to accept it | ||
/// @tparam Callable The compile-time free function pointer | ||
template <auto Callable> | ||
void connect() { | ||
m_payload = nullptr; | ||
m_function = [](const void* /*payload*/, Args... args) -> return_type { | ||
return std::invoke(Callable, std::forward<Args>(args)...); | ||
}; | ||
} | ||
|
||
/// Connect anything that is assignable to the function pointer | ||
/// @param callable The runtime value of the callable | ||
/// @note The function signature requires the first argument of the callable is `const void*`. | ||
/// i.e. if the signature of the delegate is `void(int)`, the callable's | ||
/// signature has to be `void(const void*, int)`. | ||
void connect(function_type callable) { | ||
m_payload = nullptr; | ||
m_function = callable; | ||
} | ||
|
||
/// Connect a member function to be called on an instance | ||
/// @tparam Callable The compile-time member function pointer | ||
/// @tparam Type The type of the instance the member function should be called on | ||
/// @param instance The instance on which the member function pointer should be called on | ||
/// @note @c Delegate does not assume owner ship over @p instance. You need to ensure | ||
/// it's lifetime is longer than that of @c Delegate. | ||
template <auto Callable, typename Type> | ||
void connect(const Type* instance) { | ||
m_payload = instance; | ||
m_function = [](const void* payload, Args... args) -> return_type { | ||
const auto* concretePayload = static_cast<const Type*>(payload); | ||
return std::invoke(Callable, concretePayload, | ||
std::forward<Args>(args)...); | ||
}; | ||
} | ||
|
||
/// The call operator that exposes the functionality of the @c Delegate type. | ||
/// @param args The arguments to call the contained function with | ||
/// @return Return value of the contained function | ||
return_type operator()(Args... args) const { | ||
assert(connected() && "Delegate is not connected"); | ||
return std::invoke(m_function, m_payload, std::forward<Args>(args)...); | ||
} | ||
|
||
/// Return whether this delegate is currently connected | ||
/// @return True if this delegate is connected | ||
bool connected() const { return m_function != nullptr; } | ||
|
||
/// Return whether this delegate is currently connected | ||
/// @return True if this delegate is connected | ||
operator bool() const { return connected(); } | ||
|
||
/// Disconnect this delegate, meaning it cannot be called anymore | ||
void disconnect() { | ||
m_payload = nullptr; | ||
m_function = nullptr; | ||
} | ||
|
||
private: | ||
/// Stores the instance pointer | ||
const void* m_payload{nullptr}; | ||
/// Stores the function pointer wrapping the compile time function pointer given in @c connect(). | ||
function_type m_function{nullptr}; | ||
}; | ||
} // namespace Acts |
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
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,127 @@ | ||
// This file is part of the Acts project. | ||
// | ||
// Copyright (C) 2021 CERN for the benefit of the Acts project | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
#include <boost/test/data/test_case.hpp> | ||
#include <boost/test/unit_test.hpp> | ||
|
||
#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" | ||
#include "Acts/Utilities/Delegate.hpp" | ||
|
||
#include <numeric> | ||
#include <optional> | ||
#include <random> | ||
#include <tuple> | ||
|
||
using namespace Acts; | ||
|
||
namespace bd = boost::unit_test::data; | ||
|
||
BOOST_AUTO_TEST_SUITE(DelegateTests) | ||
|
||
int sumImpl(int a, int b) { | ||
return a + b; | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(ConnectConstexprLambda) { | ||
Delegate<int(int, int)> sum; | ||
BOOST_CHECK(!sum); | ||
BOOST_CHECK(!sum.connected()); | ||
|
||
sum.connect<&sumImpl>(); | ||
|
||
BOOST_CHECK_EQUAL(sum(2, 5), 7); | ||
BOOST_CHECK_NE(sum(2, 3), 7); | ||
|
||
sum.connect([](const void*, int a, int b) -> int { return a + b; }); | ||
|
||
BOOST_CHECK(sum); | ||
BOOST_CHECK(sum.connected()); | ||
|
||
BOOST_CHECK_EQUAL(sum(2, 5), 7); | ||
BOOST_CHECK_NE(sum(2, 3), 7); | ||
} | ||
|
||
float multiply(float a, float b) { | ||
return a * b; | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(ConnectFunctionPointer) { | ||
Delegate<float(float, float)> mult; | ||
|
||
BOOST_CHECK(!mult); | ||
BOOST_CHECK(!mult.connected()); | ||
|
||
mult.connect<multiply>(); | ||
|
||
BOOST_CHECK(mult); | ||
BOOST_CHECK(mult.connected()); | ||
|
||
CHECK_CLOSE_REL(mult(2, 5.9), 2 * 5.9, 1e-6); | ||
BOOST_CHECK_NE(mult(2, 3.2), 58.9); | ||
} | ||
|
||
struct Subtractor { | ||
int v; | ||
int execute(int a) const { return a - v; } | ||
}; | ||
|
||
BOOST_AUTO_TEST_CASE(ConnectStruct) { | ||
Delegate<int(int)> sub; | ||
|
||
BOOST_CHECK(!sub); | ||
BOOST_CHECK(!sub.connected()); | ||
|
||
Subtractor s{18}; | ||
sub.connect<&Subtractor::execute>(&s); | ||
|
||
BOOST_CHECK(sub); | ||
BOOST_CHECK(sub.connected()); | ||
|
||
BOOST_CHECK_EQUAL(sub(7), 7 - 18); | ||
} | ||
|
||
int addition(const void*, int a, int b) { | ||
return a + b; | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(ConnectRuntime) { | ||
{ | ||
Delegate<int(int, int)> add; | ||
BOOST_CHECK(!add); | ||
BOOST_CHECK(!add.connected()); | ||
|
||
add.connect(&addition); | ||
BOOST_CHECK(add); | ||
BOOST_CHECK(add.connected()); | ||
|
||
BOOST_CHECK_EQUAL(add(4, 4), 8); | ||
} | ||
|
||
{ | ||
Delegate<int(int, int)> add{&addition}; | ||
|
||
BOOST_CHECK(add); | ||
BOOST_CHECK(add.connected()); | ||
|
||
BOOST_CHECK_EQUAL(add(4, 4), 8); | ||
} | ||
|
||
{ | ||
Delegate<int(int, int)> add; | ||
BOOST_CHECK(!add); | ||
BOOST_CHECK(!add.connected()); | ||
|
||
add = &addition; | ||
BOOST_CHECK(add); | ||
BOOST_CHECK(add.connected()); | ||
|
||
BOOST_CHECK_EQUAL(add(4, 4), 8); | ||
} | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |