Skip to content

Commit

Permalink
Merge branch 'main' into fix-typid-mismatch-behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
paulgessinger authored Aug 10, 2022
2 parents 5357255 + 968993e commit f28022c
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .merge-sentinel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ rules:
required_checks:
- codecov/project
- Docs / docs
- Analysis / build_debug

required_pattern:
- "Builds / *"
- "Checks / *"
- "Analysis / *"
- "CI Bridge / *"

- branch_filter:
Expand Down
63 changes: 56 additions & 7 deletions Core/include/Acts/Utilities/Delegate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ 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...);
using function_type = return_type (*)(const void *, Args...);

using function_ptr_type = return_type (*)(Args...);

template <typename T, typename C>
using isSignatureCompatible =
decltype(std::declval<T&>() = std::declval<C>());
decltype(std::declval<T &>() = std::declval<C>());

template <typename T>
using isNoFunPtr = std::enable_if_t<
not std::is_convertible_v<std::decay_t<T>, function_type>>;

public:
Delegate() = default;
Expand All @@ -53,13 +57,43 @@ class Delegate<R(Args...)> {
/// signature has to be `void(const void*, int)`.
Delegate(function_type callable) { connect(callable); }

/// Constructor with a possibly stateful function object.
/// @tparam Callable Type of the callable
/// @param callable The callable (function object or lambda)
/// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
/// it's lifetime is longer than that of @c Delegate.
template <typename Callable, typename = isNoFunPtr<Callable>>
Delegate(Callable &callable) {
connect(callable);
}

/// Constructor from rvalue reference is deleted, should catch construction
/// with temporary objects and thus invalid pointers
template <typename Callable, typename = isNoFunPtr<Callable>>
Delegate(Callable &&) = delete;

/// 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); }

/// Assignment operator with possibly stateful function object.
/// @tparam Callable Type of the callable
/// @param callable The callable (function object or lambda)
/// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
/// it's lifetime is longer than that of @c Delegate.
template <typename Callable, typename = isNoFunPtr<Callable>>
void operator=(Callable &callable) {
connect(callable);
}

/// Assignment operator from rvalue reference is deleted, should catch
/// assignment from temporary objects and thus invalid pointers
template <typename Callable, typename = isNoFunPtr<Callable>>
void operator=(Callable &&) = delete;

/// 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
Expand All @@ -73,11 +107,26 @@ class Delegate<R(Args...)> {
"Callable given does not correspond exactly to required call "
"signature");

m_function = [](const void* /*payload*/, Args... args) -> return_type {
m_function = [](const void * /*payload*/, Args... args) -> return_type {
return std::invoke(Callable, std::forward<Args>(args)...);
};
}

/// Assignment operator with possibly stateful function object.
/// @tparam Callable Type of the callable
/// @param callable The callable (function object or lambda)
/// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
/// it's lifetime is longer than that of @c Delegate.
template <typename Callable, typename = isNoFunPtr<Callable>>
void connect(Callable &callable) {
connect<&Callable::operator(), Callable>(&callable);
}

/// Connection with rvalue reference is deleted, should catch assignment from
/// temporary objects and thus invalid pointers
template <typename Callable, typename = isNoFunPtr<Callable>>
void connect(Callable &&) = delete;

/// 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*`.
Expand All @@ -95,7 +144,7 @@ class Delegate<R(Args...)> {
/// @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) {
void connect(const Type *instance) {
using member_ptr_type = return_type (Type::*)(Args...) const;

static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
Expand All @@ -104,9 +153,9 @@ class Delegate<R(Args...)> {
"signature");

m_payload = instance;
m_function = [](const void* payload, Args... args) -> return_type {
m_function = [](const void *payload, Args... args) -> return_type {
assert(payload != nullptr && "Payload is required, but not set");
const auto* concretePayload = static_cast<const Type*>(payload);
const auto *concretePayload = static_cast<const Type *>(payload);
return std::invoke(Callable, concretePayload,
std::forward<Args>(args)...);
};
Expand Down Expand Up @@ -136,7 +185,7 @@ class Delegate<R(Args...)> {

private:
/// Stores the instance pointer
const void* m_payload{nullptr};
const void *m_payload{nullptr};
/// Stores the function pointer wrapping the compile time function pointer given in @c connect().
function_type m_function{nullptr};
};
Expand Down
32 changes: 32 additions & 0 deletions Tests/UnitTests/Core/Utilities/DelegateTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,36 @@ BOOST_AUTO_TEST_CASE(DelegateReferenceMember) {
// d.connect<&SignatureTest::noModify>(&s);
}

BOOST_AUTO_TEST_CASE(StatefullLambdas) {
std::vector<int> v;

auto lambda = [&](int n) -> int {
v.push_back(n);
return v.size();
};

Delegate<int(int)> d(lambda);

BOOST_CHECK(d);
BOOST_CHECK(d.connected());
BOOST_CHECK(d(2) == 1);

d.disconnect();
d = lambda;

BOOST_CHECK(d);
BOOST_CHECK(d.connected());
BOOST_CHECK(d(2) == 2);

d.disconnect();
d.connect(lambda);

BOOST_CHECK(d);
BOOST_CHECK(d.connected());
BOOST_CHECK(d(2) == 3);

// This should not compile because of deleted && overloads
// d.connect([&](int a){ v.push_back(a); return v.size(); });
}

BOOST_AUTO_TEST_SUITE_END()
10 changes: 10 additions & 0 deletions docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ file(
../Fatras/include/*.hpp
../Plugins/include/*.hpp)

set(sphinx_build ${CMAKE_CURRENT_SOURCE_DIR}/_build)
set(doxygen_index ${CMAKE_CURRENT_SOURCE_DIR}/_build/doxygen-xml/index.xml)
set(sphinx_doctrees ${CMAKE_CURRENT_SOURCE_DIR}/_build/doctrees)
set(sphinx_html ${CMAKE_CURRENT_SOURCE_DIR}/_build/html)
set(sphinx_api ${CMAKE_CURRENT_SOURCE_DIR}/api)

add_custom_command(
OUTPUT ${doxygen_index}
Expand Down Expand Up @@ -66,6 +68,14 @@ add_custom_target(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Build full documentation")

add_custom_target(
docs-clean
COMMAND find ${sphinx_api} -mindepth 1 -type f -and -not -name "api.rst" -delete
COMMAND find ${sphinx_api} -mindepth 1 -type d -delete
COMMAND rm -rf ${sphinx_build}
COMMENT "Cleaning documentation artifacts"
)

install(
DIRECTORY ${sphinx_html}/
DESTINATION ${CMAKE_INSTALL_DOCDIR}/Acts OPTIONAL)
14 changes: 7 additions & 7 deletions docs/core/propagation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ The propagation through a geometry is based on the interaction of two different
* The **Stepper** provides the implementation of the the solution of the equation of motion (either by analytical means or through numerical integration).
* The **Navigator** keeps track of the current position in the geometry and adjusts the step size so that the stepper does not step through a surface.

Following the general Acts design, these clases do not manage their internal state via member variables, but provide a internal `State` struct which contains all relevant data and is managed by the propagator.
Following the general Acts design, these classes do not manage their internal state via member variables, but provide an internal `State` struct which contains all relevant data and is managed by the propagator.

The interaction of these two components is handled by the {class}`Acts::Propagator` class template that takes the stepper and the navigator as template parameters:

```c++
Propagator<Navigator, Stepper>
```

Additional to these mandatory components the Propagator can be equipped with **Actors** and **Aborters** to allow for custom behaviour. These are function objects that are hooked in the propagation loop. Actors just perform some action on the propagator state (e.g. the {class}`Acts::KalmanFitter` is an actor), aborts can abort propagation (e.g., the {class}`Acts::PathLimitReached`).
Additional to these mandatory components, the propagator can be equipped with **Actors** and **Aborters** to allow for custom behaviour. These are function objects that are hooked in the propagation loop. Actors just perform some action on the propagator state (e.g. the {class}`Acts::KalmanFitter` is an actor), aborts can abort propagation (e.g., the {class}`Acts::PathLimitReached`).

The propagator exposes its state to the Actors and Aborters as arguments to `operator()`. Actors must define a default-constructable `result_type`, which can be modified in each call:
The propagator exposes its state to the actors and aborters as arguments to `operator()`. Actors must define a default-constructable `result_type`, which can be modified in each call:

```c++

Expand Down Expand Up @@ -59,10 +59,10 @@ The propagator also contains a loop-protection mechanism. It estimates a circle
:::

To run the propagation, we must call the member function `propagate(...)` with the initial track parameters and the propagator options. There are several overloads to the `propagate(...)` function, which allow further customization:
* With target surface or without: The overload with a target surface automatically adds an aborter for the passed `Surface` to the `AbortList`.
* With a prepared result object or without. Without a result object, a suitable result object is default-constructed internally.
* With/without a target surface: The overload with a target surface automatically adds an aborter for the passed `Surface` to the `AbortList`.
* With/without a prepared result object: Without a result object, a suitable result object is default-constructed internally.

The result is a instance of {class}`Acts::Result`, so it can contain an error code if something went wrong, or the actual result. In the actual result, the results of the different actors can again accessed via a `get` method:
The result is an instance of {class}`Acts::Result`. It contains the actual result, or an error code in case something went wrong. In the actual result, the results of the different actors can again be accessed via a `get` method:

```c++
auto res = propagator.propagate(myParams, options);
Expand Down Expand Up @@ -118,4 +118,4 @@ By default, the {class}`Acts::EigenStepper` only uses the {class}`Acts::DefaultE

### MultiEigenStepperLoop

The {class}`Acts::MultiEigenStepper` is a extension of the {class}`Acts::EigenStepper` and designed to internally handle a multi-component state, while interfacing as a single component to the navigator. It is mainly used for the {class}`Acts::GaussianSumFitter`.
The {class}`Acts::MultiEigenStepper` is an extension of the {class}`Acts::EigenStepper` and is designed to internally handle a multi-component state, while interfacing as a single component to the navigator. It is mainly used for the {class}`Acts::GaussianSumFitter`.

0 comments on commit f28022c

Please sign in to comment.