pw_function: Make constexpr constructible
This makes pw::Function usable in classes with constexpr constructors
and makes it compatible with constinit.
Change-Id: Iba20a6e2973f2488ab767bf0aafa99bf47f5ec2e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/50361
Commit-Queue: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/pw_function/BUILD.gn b/pw_function/BUILD.gn
index 82009b1..17befd1 100644
--- a/pw_function/BUILD.gn
+++ b/pw_function/BUILD.gn
@@ -63,7 +63,10 @@
}
pw_test("function_test") {
- deps = [ ":pw_function" ]
+ deps = [
+ ":pw_function",
+ dir_pw_polyfill,
+ ]
sources = [ "function_test.cc" ]
}
diff --git a/pw_function/docs.rst b/pw_function/docs.rst
index 8a2cf76..a2cb0c5 100644
--- a/pw_function/docs.rst
+++ b/pw_function/docs.rst
@@ -54,6 +54,23 @@
function();
}
+``pw::Function``'s default constructor is ``constexpr``, so default-constructed
+functions may be used in classes with ``constexpr`` constructors and in
+``constinit`` expressions.
+
+.. code-block:: c++
+
+ class MyClass {
+ public:
+ // Default construction of a pw::Function is constexpr.
+ constexpr MyClass() { ... }
+
+ pw::Function<void(int)> my_function;
+ };
+
+ // pw::Function and classes that use it may be constant initialized.
+ constinit MyClass instance;
+
Storage
-------
By default, a ``Function`` stores its callable inline within the object. The
diff --git a/pw_function/function_test.cc b/pw_function/function_test.cc
index f76181c..fcc222d 100644
--- a/pw_function/function_test.cc
+++ b/pw_function/function_test.cc
@@ -15,10 +15,14 @@
#include "pw_function/function.h"
#include "gtest/gtest.h"
+#include "pw_polyfill/language_feature_macros.h"
namespace pw {
namespace {
+// Ensure that Function can be constant initialized.
+[[maybe_unused]] PW_CONSTINIT Function<void()> can_be_constant_initialized;
+
int Multiply(int a, int b) { return a * b; }
TEST(Function, OperatorCall) {
diff --git a/pw_function/public/pw_function/internal/function.h b/pw_function/public/pw_function/internal/function.h
index 1a28c3b..f6062aa 100644
--- a/pw_function/public/pw_function/internal/function.h
+++ b/pw_function/public/pw_function/internal/function.h
@@ -54,14 +54,15 @@
template <typename Return, typename... Args>
class FunctionTarget {
public:
- FunctionTarget() = default;
- virtual ~FunctionTarget() = default;
+ constexpr FunctionTarget() = default;
FunctionTarget(const FunctionTarget&) = delete;
FunctionTarget(FunctionTarget&&) = delete;
FunctionTarget& operator=(const FunctionTarget&) = delete;
FunctionTarget& operator=(FunctionTarget&&) = delete;
+ virtual void Destroy() {}
+
virtual bool IsNull() const = 0;
// Invoke the callable stored by the function target.
@@ -69,6 +70,9 @@
// Move initialize the function target to a provided location.
virtual void MoveInitializeTo(void* ptr) = 0;
+
+ protected:
+ ~FunctionTarget() = default; // The destructor is never called.
};
// A function target that does not store any callable. Attempting to invoke it
@@ -76,8 +80,7 @@
template <typename Return, typename... Args>
class NullFunctionTarget final : public FunctionTarget<Return, Args...> {
public:
- NullFunctionTarget() = default;
- ~NullFunctionTarget() final = default;
+ constexpr NullFunctionTarget() = default;
NullFunctionTarget(const NullFunctionTarget&) = delete;
NullFunctionTarget(NullFunctionTarget&&) = delete;
@@ -98,7 +101,7 @@
explicit InlineFunctionTarget(Callable&& callable)
: callable_(std::move(callable)) {}
- ~InlineFunctionTarget() final = default;
+ void Destroy() final { callable_.~Callable(); }
InlineFunctionTarget(const InlineFunctionTarget&) = delete;
InlineFunctionTarget& operator=(const InlineFunctionTarget&) = delete;
@@ -131,7 +134,7 @@
new (address_) Callable(std::move(callable));
}
- ~MemoryFunctionTarget() final {
+ void Destroy() final {
// Multiple MemoryFunctionTargets may have referred to the same callable
// (due to moves), but only one can have a valid pointer to it. The owner is
// responsible for destructing the callable.
@@ -182,7 +185,7 @@
template <size_t kSizeBytes, typename Return, typename... Args>
class FunctionTargetHolder {
public:
- FunctionTargetHolder() = default;
+ constexpr FunctionTargetHolder() : null_function_{} {}
FunctionTargetHolder(const FunctionTargetHolder&) = delete;
FunctionTargetHolder(FunctionTargetHolder&&) = delete;
@@ -193,7 +196,7 @@
using NullFunctionTarget = NullFunctionTarget<Return, Args...>;
static_assert(sizeof(NullFunctionTarget) <= kSizeBytes,
"NullFunctionTarget must fit within FunctionTargetHolder");
- new (&bits_) NullFunctionTarget;
+ new (&null_function_) NullFunctionTarget;
}
// Initializes an InlineFunctionTarget with the callable, failing if it is too
@@ -218,7 +221,7 @@
new (&bits_) MemoryFunctionTarget(storage, std::move(callable));
}
- void DestructTarget() { target().~Target(); }
+ void DestructTarget() { target().Destroy(); }
// Initializes the function target within this callable from another target
// holder's function target.
@@ -234,8 +237,13 @@
}
private:
- // Storage for an implementation of the FunctionTarget interface.
- FunctionStorage<kSizeBytes> bits_;
+ // Storage for an implementation of the FunctionTarget interface. Make this a
+ // union with NullFunctionTarget so that the constexpr constructor can
+ // initialize null_function_ directly.
+ union {
+ FunctionStorage<kSizeBytes> bits_;
+ NullFunctionTarget<Return, Args...> null_function_;
+ };
};
template <typename Return, typename... Args>
@@ -244,7 +252,7 @@
template <typename Return, typename... Args>
class Function<Return(Args...)> {
public:
- constexpr Function() { holder_.InitializeNullTarget(); }
+ constexpr Function() = default;
constexpr Function(std::nullptr_t) : Function() {}
template <typename Callable>