pw_function: Standard callback API

This implements pw::Function, a standard wrapper for callable objects.

This CL is currently provided as a proof-of-concept implementation based
on fbl::Function, and does not necessarily represent the final design of
pw::Function.

Change-Id: Ie2f5714b9711a08de878471e8f31fc46b26d36e8
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/46080
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Alexei Frolov <frolv@google.com>
diff --git a/pw_function/BUILD b/pw_function/BUILD
new file mode 100644
index 0000000..768cc3c
--- /dev/null
+++ b/pw_function/BUILD
@@ -0,0 +1,47 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+    "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "config",
+    hdrs = ["public/pw_function/config.h"],
+    includes = ["public"],
+)
+
+pw_cc_library(
+    name = "pw_function",
+    srcs = ["public/pw_function/internal/function.h"],
+    hdrs = ["public/pw_function/function.h"],
+    includes = ["public"],
+    deps = [
+        ":config",
+        "//pw_assert",
+        "//pw_preprocessor",
+    ],
+)
+
+pw_cc_test(
+    name = "function_test",
+    srcs = ["function_test.cc"],
+    deps = [":pw_function"],
+)
diff --git a/pw_function/BUILD.gn b/pw_function/BUILD.gn
new file mode 100644
index 0000000..82009b1
--- /dev/null
+++ b/pw_function/BUILD.gn
@@ -0,0 +1,117 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_bloat/bloat.gni")
+import("$dir_pw_build/module_config.gni")
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+declare_args() {
+  # The build target that overrides the default configuration options for this
+  # module. This should point to a source set that provides defines through a
+  # public config (which may -include a file or add defines directly).
+  pw_function_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
+}
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+pw_source_set("config") {
+  public = [ "public/pw_function/config.h" ]
+  public_configs = [ ":public_include_path" ]
+  public_deps = [ pw_function_CONFIG ]
+  visibility = [ ":*" ]
+}
+
+pw_source_set("pw_function") {
+  public_configs = [ ":public_include_path" ]
+  public_deps = [
+    ":config",
+    dir_pw_assert,
+    dir_pw_preprocessor,
+  ]
+  public = [ "public/pw_function/function.h" ]
+  sources = [ "public/pw_function/internal/function.h" ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+  report_deps = [
+    ":callable_size",
+    ":function_size",
+  ]
+}
+
+pw_test_group("tests") {
+  tests = [ ":function_test" ]
+}
+
+pw_test("function_test") {
+  deps = [ ":pw_function" ]
+  sources = [ "function_test.cc" ]
+}
+
+pw_size_report("function_size") {
+  title = "Pigweed function size report"
+
+  binaries = [
+    {
+      target = "size_report:basic_function"
+      base = "size_report:pointer_base"
+      label = "Simple pw::Function vs. function pointer"
+    },
+  ]
+}
+
+pw_size_report("callable_size") {
+  title = "Size comparison of callable objects"
+
+  binaries = [
+    {
+      target = "size_report:callable_size_function_pointer"
+      base = "size_report:callable_size_base"
+      label = "Function pointer"
+    },
+    {
+      target = "size_report:callable_size_static_lambda"
+      base = "size_report:callable_size_base"
+      label = "Static lambda (operator+)"
+    },
+    {
+      target = "size_report:callable_size_simple_lambda"
+      base = "size_report:callable_size_base"
+      label = "Non-capturing lambda"
+    },
+    {
+      target = "size_report:callable_size_capturing_lambda"
+      base = "size_report:callable_size_base"
+      label = "Simple capturing lambda"
+    },
+    {
+      target = "size_report:callable_size_multi_capturing_lambda"
+      base = "size_report:callable_size_base"
+      label = "Multi-argument capturing lambda"
+    },
+    {
+      target = "size_report:callable_size_custom_class"
+      base = "size_report:callable_size_base"
+      label = "Custom class"
+    },
+  ]
+}
diff --git a/pw_function/CMakeLists.txt b/pw_function/CMakeLists.txt
new file mode 100644
index 0000000..6f6e275
--- /dev/null
+++ b/pw_function/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_auto_add_simple_module(pw_function
+  PUBLIC_DEPS
+    pw_assert
+    pw_preprocessor
+)
diff --git a/pw_function/docs.rst b/pw_function/docs.rst
new file mode 100644
index 0000000..fa6e68e
--- /dev/null
+++ b/pw_function/docs.rst
@@ -0,0 +1,178 @@
+.. _module-pw_function:
+
+-----------
+pw_function
+-----------
+The function module provides a standard, general-purpose API for wrapping
+callable objects.
+
+.. note::
+  This module is under construction and its API is not complete.
+
+Overview
+========
+
+Basic usage
+-----------
+``pw_function`` defines the ``pw::Function`` class. A ``Function`` is a
+move-only callable wrapper constructable from any callable object. Functions
+are templated on the signature of the callable they store.
+
+Functions implement the call operator --- invoking the object will forward to
+the stored callable.
+
+.. code-block:: c++
+
+  int Add(int a, int b) { return a + b; }
+
+  // Construct a Function object from a function pointer.
+  pw::Function<int(int, int)> add_function(Add);
+
+  // Invoke the function object.
+  int result = add_function(3, 5);
+  EXPECT_EQ(result, 8);
+
+  // Construct a function from a lambda.
+  pw::Function<int(int)> negate([](int value) { return -value; });
+  EXPECT_EQ(negate(27), -27);
+
+Functions are nullable. Invoking a null function triggers a runtime assert.
+
+.. code-block:: c++
+
+  // A function intialized without a callable is implicitly null.
+  pw::Function<void()> null_function;
+
+  // Null functions may also be explicitly created or set.
+  pw::Function<void()> explicit_null_function(nullptr);
+
+  pw::Function<void()> function([]() {});  // Valid (non-null) function.
+  function = nullptr;  // Set to null, clearing the stored callable.
+
+  // Functions are comparable to nullptr.
+  if (function != nullptr) {
+    function();
+  }
+
+Storage
+-------
+By default, a ``Function`` stores its callable inline within the object. The
+inline storage size defaults to the size of two pointers, but is configurable
+through the build system. The size of a ``Function`` object is equivalent to its
+inline storage size.
+
+Attempting to construct a function from a callable larger than its inline size
+is a compile-time error.
+
+.. admonition:: Inline storage size
+
+  The default inline size of two pointers is sufficient to store most common
+  callable objects, including function pointers, simple non-capturing and
+  capturing lambdas, and lightweight custom classes.
+
+.. code-block:: c++
+
+  // The lambda is moved into the function's internal storage.
+  pw::Function<int(int, int)> subtract([](int a, int b) { return a - b; });
+
+  // Functions can be also be constructed from custom classes that implement
+  // operator(). This particular object is large (8 ints of space).
+  class MyCallable {
+   public:
+    int operator()(int value);
+
+   private:
+    int data_[8];
+  };
+
+  // Compiler error: sizeof(MyCallable) exceeds function's inline storage size.
+  pw::Function<int(int)> function((MyCallable()));
+
+..
+  For larger callables, a ``Function`` can be constructed with an external buffer
+  in which the callable should be stored. The user must ensure that the lifetime
+  of the buffer exceeds that of the function object.
+
+  .. code-block:: c++
+
+    // Initialize a function with an external 16-byte buffer in which to store its
+    // callable. The callable will be stored in the buffer regardless of whether
+    // it fits inline.
+    pw::FunctionStorage<16> storage;
+    pw::Function<int()> get_random_number([]() { return 4; }, storage);
+
+  .. admonition:: External storage
+
+    Functions which use external storage still take up the configured inline
+    storage size, which should be accounted for when storing function objects.
+
+In the future, ``pw::Function`` may support dynamic allocation of callable
+storage using the system allocator. This operation will always be explicit.
+
+API usage
+=========
+
+Implementation-side
+-------------------
+When implementing an API which takes a callback, a ``Function`` can be used in
+place of a function pointer or equivalent callable.
+
+.. code-block:: c++
+
+  // Before:
+  void DoTheThing(int arg, void (*callback)(int result));
+
+  // After. Note that it is possible to have parameter names within the function
+  // signature template for clarity.
+  void DoTheThing(int arg, pw::Function<void(int result)> callback);
+
+An API can accept a function either by value or by reference. If taken by value,
+the implementation is responsible for managing the function by moving it into an
+appropriate location.
+
+.. admonition:: Value or reference?
+
+  It is preferable for APIs to take functions by value rather than by reference.
+  This provides callers of the API with a more convenient interface, as well as
+  making their lives easier by not requiring management of resources or
+  lifetimes.
+
+Caller-side
+-----------
+When calling an API which takes a function by reference, the standard pattern is
+to implicitly construct the function in place from a callable object. Simply
+pass the desired callable directly to the API.
+
+.. code-block:: c++
+
+  // Implicitly initialize a Function from a capturing lambda.
+  DoTheThing(42, [this](int result) { result_ = result; });
+
+Size reports
+============
+
+Function class
+--------------
+The following size report compares an API using a ``pw::Function`` to a
+traditional function pointer.
+
+.. include:: function_size
+
+Callable sizes
+--------------
+The table below demonstrates typical sizes of various callable types, which can
+be used as a reference when sizing external buffers for ``Function`` objects.
+
+.. include:: callable_size
+
+Design
+======
+``pw::Function`` is based largely on
+`fbl::Function <https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/fbl/include/fbl/function.h>`_
+from Fuchsia with some changes to make it more suitable for embedded
+development.
+
+Functions are moveable, but not copyable. This allows them to store and manage
+callables without having to perform bookkeeping such as reference counting, and
+avoids any reliance on dynamic memory management. The result is a simpler
+implementation which is easy to conceptualize and use in an embedded context.
diff --git a/pw_function/function_test.cc b/pw_function/function_test.cc
new file mode 100644
index 0000000..f76181c
--- /dev/null
+++ b/pw_function/function_test.cc
@@ -0,0 +1,226 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_function/function.h"
+
+#include "gtest/gtest.h"
+
+namespace pw {
+namespace {
+
+int Multiply(int a, int b) { return a * b; }
+
+TEST(Function, OperatorCall) {
+  Function<int(int, int)> multiply(Multiply);
+  EXPECT_EQ(multiply(3, 7), 21);
+}
+
+void CallbackAdd(int a, int b, pw::Function<void(int sum)> callback) {
+  callback(a + b);
+}
+
+int add_result = -1;
+
+void free_add_callback(int sum) { add_result = sum; }
+
+TEST(Function, ConstructInPlace_FreeFunction) {
+  add_result = -1;
+  CallbackAdd(25, 17, free_add_callback);
+  EXPECT_EQ(add_result, 42);
+}
+
+TEST(Function, ConstructInPlace_NonCapturingLambda) {
+  add_result = -1;
+  CallbackAdd(25, 18, [](int sum) { add_result = sum; });
+  EXPECT_EQ(add_result, 43);
+}
+
+TEST(Function, ConstructInPlace_CapturingLambda) {
+  int result = -1;
+  CallbackAdd(25, 19, [&](int sum) { result = sum; });
+  EXPECT_EQ(result, 44);
+}
+
+class CallableObject {
+ public:
+  CallableObject(int* result) : result_(result) {}
+
+  CallableObject(CallableObject&& other) = default;
+  CallableObject& operator=(CallableObject&& other) = default;
+
+  void operator()(int sum) { *result_ = sum; }
+
+ private:
+  int* result_;
+};
+
+TEST(Function, ConstructInPlace_CallableObject) {
+  int result = -1;
+  CallbackAdd(25, 20, CallableObject(&result));
+  EXPECT_EQ(result, 45);
+}
+
+class MemberFunctionTest : public ::testing::Test {
+ protected:
+  MemberFunctionTest() : result_(-1) {}
+
+  void set_result(int result) { result_ = result; }
+
+  int result_;
+};
+
+TEST_F(MemberFunctionTest, ConstructInPlace_Lambda) {
+  CallbackAdd(25, 21, [this](int sum) { set_result(sum); });
+  EXPECT_EQ(result_, 46);
+}
+
+TEST(Function, Null_OperatorBool) {
+  Closure implicit_null;
+  Closure explicit_null(nullptr);
+  Closure assigned_null = nullptr;
+  Closure not_null([]() {});
+
+  EXPECT_FALSE(bool(implicit_null));
+  EXPECT_FALSE(bool(explicit_null));
+  EXPECT_FALSE(bool(assigned_null));
+  EXPECT_TRUE(bool(not_null));
+
+  EXPECT_TRUE(!implicit_null);
+  EXPECT_TRUE(!explicit_null);
+  EXPECT_TRUE(!assigned_null);
+  EXPECT_FALSE(!not_null);
+}
+
+TEST(Function, Null_OperatorEquals) {
+  Closure implicit_null;
+  Closure explicit_null(nullptr);
+  Closure assigned_null = nullptr;
+  Closure not_null([]() {});
+
+  EXPECT_TRUE(implicit_null == nullptr);
+  EXPECT_TRUE(explicit_null == nullptr);
+  EXPECT_TRUE(assigned_null == nullptr);
+  EXPECT_TRUE(not_null != nullptr);
+
+  EXPECT_FALSE(implicit_null != nullptr);
+  EXPECT_FALSE(explicit_null != nullptr);
+  EXPECT_FALSE(assigned_null != nullptr);
+  EXPECT_FALSE(not_null == nullptr);
+}
+
+TEST(Function, Null_Set) {
+  Closure function = []() {};
+  EXPECT_NE(function, nullptr);
+  function = nullptr;
+  EXPECT_EQ(function, nullptr);
+}
+
+void DoNothing() {}
+
+TEST(Function, Null_FunctionPointer) {
+  void (*ptr)() = DoNothing;
+  Closure not_null(ptr);
+  EXPECT_NE(not_null, nullptr);
+  ptr = nullptr;
+  Closure is_null(ptr);
+  EXPECT_EQ(is_null, nullptr);
+}
+
+TEST(Function, Move_Null) {
+  Closure moved;
+  EXPECT_EQ(moved, nullptr);
+  Closure function(std::move(moved));
+  EXPECT_EQ(function, nullptr);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+TEST(Function, MoveAssign_Null) {
+  Closure moved;
+  EXPECT_EQ(moved, nullptr);
+  Closure function = std::move(moved);
+  EXPECT_EQ(function, nullptr);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+TEST(Function, Move_Inline) {
+  Function<int(int, int)> moved(Multiply);
+  EXPECT_NE(moved, nullptr);
+  Function<int(int, int)> multiply(std::move(moved));
+  EXPECT_EQ(multiply(3, 3), 9);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+TEST(Function, MoveAssign_Inline) {
+  Function<int(int, int)> moved(Multiply);
+  EXPECT_NE(moved, nullptr);
+  Function<int(int, int)> multiply = std::move(moved);
+  EXPECT_EQ(multiply(3, 3), 9);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+class MoveTracker {
+ public:
+  MoveTracker() : move_count_(0) {}
+
+  MoveTracker(MoveTracker&& other) : move_count_(other.move_count_ + 1) {}
+  MoveTracker& operator=(MoveTracker&& other) = default;
+
+  int operator()() const { return move_count_; }
+
+ private:
+  int move_count_;
+};
+
+TEST(Function, Move_CustomObject) {
+  Function<int()> moved((MoveTracker()));
+  EXPECT_EQ(moved(), 2);  // internally moves twice on construction
+  Function<int()> tracker(std::move(moved));
+  EXPECT_EQ(tracker(), 3);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+TEST(Function, MoveAssign_CustomObject) {
+  Function<int()> moved((MoveTracker()));
+  EXPECT_EQ(moved(), 2);  // internally moves twice on construction
+  Function<int()> tracker = std::move(moved);
+  EXPECT_EQ(tracker(), 3);
+
+// Ignore use-after-move.
+#ifndef __clang_analyzer__
+  EXPECT_EQ(moved, nullptr);
+#endif  // __clang_analyzer__
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_function/public/pw_function/config.h b/pw_function/public/pw_function/config.h
new file mode 100644
index 0000000..b98db4b
--- /dev/null
+++ b/pw_function/public/pw_function/config.h
@@ -0,0 +1,47 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Configuration macros for the function module.
+#pragma once
+
+#include <cstddef>
+
+// The maximum size of a callable that can be inlined within a function. This is
+// also the size of the Function object itself. Callables larger than this are
+// stored externally to the function.
+//
+// This defaults to 2 pointers, which is capable of storing common callables
+// such as function pointers and simple lambdas.
+#ifndef PW_FUNCTION_INLINE_CALLABLE_SIZE
+#define PW_FUNCTION_INLINE_CALLABLE_SIZE (2 * sizeof(void*))
+#endif  // PW_FUNCTION_INLINE_CALLABLE_SIZE
+
+static_assert(PW_FUNCTION_INLINE_CALLABLE_SIZE > 0 &&
+              PW_FUNCTION_INLINE_CALLABLE_SIZE % alignof(void*) == 0);
+
+// Whether functions should allocate memory dynamically (using operator new) if
+// a callable is larger than the inline size.
+//
+// NOTE: This is not currently used.
+#ifndef PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION
+#define PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION 0
+#endif  // PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION
+
+namespace pw::function_internal::config {
+
+inline constexpr size_t kInlineCallableSize = PW_FUNCTION_INLINE_CALLABLE_SIZE;
+inline constexpr bool kEnableDynamicAllocation =
+    PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION;
+
+}  // namespace pw::function_internal::config
diff --git a/pw_function/public/pw_function/function.h b/pw_function/public/pw_function/function.h
new file mode 100644
index 0000000..ea086c4
--- /dev/null
+++ b/pw_function/public/pw_function/function.h
@@ -0,0 +1,73 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_function/internal/function.h"
+
+namespace pw {
+
+// pw::Function is a wrapper for an aribtrary callable object. It can be used by
+// callback-based APIs to allow callers to provide any type of callable.
+//
+// Example:
+//
+//   template <typename T>
+//   bool All(const pw::Vector<T>& items,
+//            pw::Function<bool(const T& item)> predicate) {
+//     for (const T& item : items) {
+//       if (!predicate(item)) {
+//         return false;
+//       }
+//     }
+//     return true;
+//   }
+//
+//   bool ElementsArePostive(const pw::Vector<int>& items) {
+//     return All(items, [](const int& i) { return i > 0; });
+//   }
+//
+//   bool IsEven(const int& i) { return i % 2 == 0; }
+//
+//   bool ElementsAreEven(const pw::Vector<int>& items) {
+//     return All(items, IsEven);
+//   }
+//
+template <typename T>
+using Function = function_internal::Function<T>;
+
+// A Closure is a function that does not take any arguments and returns nothing.
+using Closure = Function<void()>;
+
+// nullptr comparisions for functions.
+template <typename T>
+bool operator==(const Function<T>& f, std::nullptr_t) {
+  return !f;
+}
+
+template <typename T>
+bool operator!=(const Function<T>& f, std::nullptr_t) {
+  return !!f;
+}
+
+template <typename T>
+bool operator==(std::nullptr_t, const Function<T>& f) {
+  return !f;
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t, const Function<T>& f) {
+  return !!f;
+}
+
+}  // namespace pw
diff --git a/pw_function/public/pw_function/internal/function.h b/pw_function/public/pw_function/internal/function.h
new file mode 100644
index 0000000..5bf5afa
--- /dev/null
+++ b/pw_function/public/pw_function/internal/function.h
@@ -0,0 +1,320 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <cstddef>
+#include <new>
+#include <utility>
+
+#include "pw_assert/assert.h"
+#include "pw_function/config.h"
+#include "pw_preprocessor/compiler.h"
+
+namespace pw::function_internal {
+
+template <typename T, typename Comparison = bool>
+struct NullEq {
+  static constexpr bool Test(const T&) { return false; }
+};
+
+// Partial specialization for values of T comparable to nullptr.
+template <typename T>
+struct NullEq<T, decltype(std::declval<T>() == nullptr)> {
+  // This is intended to be used for comparing function pointers to nullptr, but
+  // the specialization also matches Ts that implicitly convert to a function
+  // pointer, such as function types. The compiler may then complain that the
+  // comparison is false, as the address is known at compile time and cannot be
+  // nullptr. Silence this warning. (The compiler will optimize out the
+  // comparison.)
+  PW_MODIFY_DIAGNOSTICS_PUSH();
+  PW_MODIFY_DIAGNOSTIC(ignored, "-Waddress");
+  static constexpr bool Test(const T& v) { return v == nullptr; }
+  PW_MODIFY_DIAGNOSTICS_POP();
+};
+
+// Tests whether a value is considered to be null.
+template <typename T>
+static constexpr bool IsNull(const T& v) {
+  return NullEq<T>::Test(v);
+}
+
+// FunctionTarget is an interface for storing a callable object and providing a
+// way to invoke it.
+template <typename Return, typename... Args>
+class FunctionTarget {
+ public:
+  FunctionTarget() = default;
+  virtual ~FunctionTarget() = default;
+
+  FunctionTarget(const FunctionTarget&) = delete;
+  FunctionTarget(FunctionTarget&&) = delete;
+  FunctionTarget& operator=(const FunctionTarget&) = delete;
+  FunctionTarget& operator=(FunctionTarget&&) = delete;
+
+  virtual bool IsNull() const = 0;
+
+  // Invoke the callable stored by the function target.
+  virtual Return operator()(Args&&... args) const = 0;
+
+  // Move initialize the function target to a provided location.
+  virtual void MoveInitializeTo(void* ptr) = 0;
+};
+
+// A function target that does not store any callable. Attempting to invoke it
+// results in a crash.
+template <typename Return, typename... Args>
+class NullFunctionTarget final : public FunctionTarget<Return, Args...> {
+ public:
+  NullFunctionTarget() = default;
+  ~NullFunctionTarget() final = default;
+
+  NullFunctionTarget(const NullFunctionTarget&) = delete;
+  NullFunctionTarget(NullFunctionTarget&&) = delete;
+  NullFunctionTarget& operator=(const NullFunctionTarget&) = delete;
+  NullFunctionTarget& operator=(NullFunctionTarget&&) = delete;
+
+  bool IsNull() const final { return true; }
+
+  Return operator()(Args&&...) const final { PW_ASSERT(false); }
+
+  void MoveInitializeTo(void* ptr) final { new (ptr) NullFunctionTarget(); }
+};
+
+// Function target that stores a callable as a member within the class.
+template <typename Callable, typename Return, typename... Args>
+class InlineFunctionTarget final : public FunctionTarget<Return, Args...> {
+ public:
+  explicit InlineFunctionTarget(Callable&& callable)
+      : callable_(std::move(callable)) {}
+
+  ~InlineFunctionTarget() final = default;
+
+  InlineFunctionTarget(const InlineFunctionTarget&) = delete;
+  InlineFunctionTarget& operator=(const InlineFunctionTarget&) = delete;
+
+  InlineFunctionTarget(InlineFunctionTarget&& other)
+      : callable_(std::move(other.callable_)) {}
+  InlineFunctionTarget& operator=(InlineFunctionTarget&&) = default;
+
+  bool IsNull() const final { return false; }
+
+  Return operator()(Args&&... args) const final {
+    return callable_(std::forward<Args>(args)...);
+  }
+
+  void MoveInitializeTo(void* ptr) final {
+    new (ptr) InlineFunctionTarget(std::move(*this));
+  }
+
+ private:
+  // This must be mutable to support custom objects that implement operator() in
+  // a non-const way.
+  mutable Callable callable_;
+};
+
+// Function target which stores a callable at a provided location in memory.
+// The creating context must ensure that the region is properly sized and
+// aligned for the callable.
+template <typename Callable, typename Return, typename... Args>
+class MemoryFunctionTarget final : public FunctionTarget<Return, Args...> {
+ public:
+  MemoryFunctionTarget(void* address, Callable&& callable) : address_(address) {
+    new (address_) Callable(std::move(callable));
+  }
+
+  ~MemoryFunctionTarget() 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.
+    if (address_ != nullptr) {
+      callable().~Callable();
+    }
+  }
+
+  MemoryFunctionTarget(const MemoryFunctionTarget&) = delete;
+  MemoryFunctionTarget& operator=(const MemoryFunctionTarget&) = delete;
+
+  // Transfer the pointer to the initialized callable to this object without
+  // reinitializing the callable, clearing the address from the other.
+  MemoryFunctionTarget(MemoryFunctionTarget&& other)
+      : address_(other.address_) {
+    other.address_ = nullptr;
+  }
+  MemoryFunctionTarget& operator=(MemoryFunctionTarget&&) = default;
+
+  bool IsNull() const final { return false; }
+
+  Return operator()(Args&&... args) const final {
+    return callable()(std::forward<Args>(args)...);
+  }
+
+  void MoveInitializeTo(void* ptr) final {
+    new (ptr) MemoryFunctionTarget(std::move(*this));
+  }
+
+ private:
+  Callable& callable() {
+    return *std::launder(reinterpret_cast<Callable*>(address_));
+  }
+  const Callable& callable() const {
+    return *std::launder(reinterpret_cast<const Callable*>(address_));
+  }
+
+  void* address_;
+};
+
+template <size_t kSizeBytes>
+using FunctionStorage =
+    std::aligned_storage_t<kSizeBytes, alignof(std::max_align_t)>;
+
+// A FunctionTargetHolder stores an instance of a FunctionTarget implementation.
+//
+// The concrete implementation is initialized in an internal buffer by calling
+// one of the initialization functions. After initialization, all
+// implementations are accessed through the virtual FunctionTarget base.
+template <size_t kSizeBytes, typename Return, typename... Args>
+class FunctionTargetHolder {
+ public:
+  FunctionTargetHolder() = default;
+
+  FunctionTargetHolder(const FunctionTargetHolder&) = delete;
+  FunctionTargetHolder(FunctionTargetHolder&&) = delete;
+  FunctionTargetHolder& operator=(const FunctionTargetHolder&) = delete;
+  FunctionTargetHolder& operator=(FunctionTargetHolder&&) = delete;
+
+  constexpr void InitializeNullTarget() {
+    using NullFunctionTarget = NullFunctionTarget<Return, Args...>;
+    static_assert(sizeof(NullFunctionTarget) <= kSizeBytes,
+                  "NullFunctionTarget must fit within FunctionTargetHolder");
+    new (&bits_) NullFunctionTarget;
+  }
+
+  // Initializes an InlineFunctionTarget with the callable, failing if it is too
+  // large.
+  template <typename Callable>
+  void InitializeInlineTarget(Callable callable) {
+    using InlineFunctionTarget =
+        InlineFunctionTarget<Callable, Return, Args...>;
+    static_assert(sizeof(InlineFunctionTarget) <= kSizeBytes,
+                  "Inline callable must fit within FunctionTargetHolder");
+    new (&bits_) InlineFunctionTarget(std::move(callable));
+  }
+
+  // Initializes a MemoryTarget that stores the callable at the provided
+  // location.
+  template <typename Callable>
+  void InitializeMemoryTarget(Callable callable, void* storage) {
+    using MemoryFunctionTarget =
+        MemoryFunctionTarget<Callable, Return, Args...>;
+    static_assert(sizeof(MemoryFunctionTarget) <= kSizeBytes,
+                  "MemoryFunctionTarget must fit within FunctionTargetHolder");
+    new (&bits_) MemoryFunctionTarget(storage, std::move(callable));
+  }
+
+  void DestructTarget() { target().~Target(); }
+
+  // Initializes the function target within this callable from another target
+  // holder's function target.
+  void MoveInitializeTargetFrom(FunctionTargetHolder& other) {
+    other.target().MoveInitializeTo(&bits_);
+  }
+
+  // The stored implementation is accessed by punning to the virtual base class.
+  using Target = FunctionTarget<Return, Args...>;
+  Target& target() { return *std::launder(reinterpret_cast<Target*>(&bits_)); }
+  const Target& target() const {
+    return *std::launder(reinterpret_cast<const Target*>(&bits_));
+  }
+
+ private:
+  // Storage for an implementation of the FunctionTarget interface.
+  FunctionStorage<kSizeBytes> bits_;
+};
+
+template <typename Return, typename... Args>
+class Function;
+
+template <typename Return, typename... Args>
+class Function<Return(Args...)> {
+ public:
+  constexpr Function() { holder_.InitializeNullTarget(); }
+  constexpr Function(std::nullptr_t) : Function() {}
+
+  template <typename Callable>
+  Function(Callable callable) {
+    if (IsNull(callable)) {
+      holder_.InitializeNullTarget();
+    } else {
+      holder_.InitializeInlineTarget(std::move(callable));
+    }
+  }
+
+  Function(Function&& other) {
+    holder_.MoveInitializeTargetFrom(other.holder_);
+    other.holder_.InitializeNullTarget();
+  }
+
+  Function& operator=(Function&& other) {
+    holder_.DestructTarget();
+    holder_.MoveInitializeTargetFrom(other.holder_);
+    other.holder_.InitializeNullTarget();
+    return *this;
+  }
+
+  Function& operator=(std::nullptr_t) {
+    holder_.DestructTarget();
+    holder_.InitializeNullTarget();
+    return *this;
+  }
+
+  template <typename Callable>
+  Function& operator=(Callable callable) {
+    holder_.DestructTarget();
+    InitializeTarget(std::move(callable));
+    return *this;
+  }
+
+  ~Function() { holder_.DestructTarget(); }
+
+  Return operator()(Args&&... args) const {
+    return holder_.target()(std::forward<Args>(args)...);
+  };
+
+  explicit operator bool() const { return !holder_.target().IsNull(); }
+
+ private:
+  // TODO(frolv): This is temporarily private while the API is worked out.
+  template <typename Callable, size_t kSizeBytes>
+  Function(Callable&& callable, FunctionStorage<kSizeBytes>& storage)
+      : Function(callable, &storage) {
+    static_assert(sizeof(Callable) <= kSizeBytes,
+                  "pw::Function callable does not fit into provided storage");
+  }
+
+  // Constructs a function that stores its callable at the provided location.
+  // Public constructors wrapping this must ensure that the memory region is
+  // capable of storing the callable in terms of both size and alignment.
+  template <typename Callable>
+  Function(Callable&& callable, void* storage) {
+    if (IsNull(callable)) {
+      holder_.InitializeNullTarget();
+    } else {
+      holder_.InitializeMemoryTarget(std::move(callable), storage);
+    }
+  }
+
+  FunctionTargetHolder<config::kInlineCallableSize, Return, Args...> holder_;
+};
+
+}  // namespace pw::function_internal
diff --git a/pw_function/size_report/BUILD b/pw_function/size_report/BUILD
new file mode 100644
index 0000000..072ec6f
--- /dev/null
+++ b/pw_function/size_report/BUILD
@@ -0,0 +1,49 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_binary",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_binary(
+    name = "pointer_base",
+    srcs = ["pointer_base.cc"],
+    deps = [
+        "//pw_bloat:bloat_this_binary",
+        "//pw_function",
+    ],
+)
+
+pw_cc_binary(
+    name = "basic_function",
+    srcs = ["basic_function.cc"],
+    deps = [
+        "//pw_bloat:bloat_this_binary",
+        "//pw_function",
+    ],
+)
+
+pw_cc_binary(
+    name = "callable_size",
+    srcs = ["callable_size.cc"],
+    deps = [
+        "//pw_bloat:bloat_this_binary",
+        "//pw_function",
+    ],
+)
diff --git a/pw_function/size_report/BUILD.gn b/pw_function/size_report/BUILD.gn
new file mode 100644
index 0000000..232fbb9
--- /dev/null
+++ b/pw_function/size_report/BUILD.gn
@@ -0,0 +1,74 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+_deps = [
+  "$dir_pw_bloat:bloat_this_binary",
+  "..:pw_function",
+]
+
+pw_executable("pointer_base") {
+  sources = [ "pointer_base.cc" ]
+  deps = _deps
+}
+
+pw_executable("basic_function") {
+  sources = [ "basic_function.cc" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_base") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_BASE=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_function_pointer") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_FUNCTION_POINTER=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_static_lambda") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_STATIC_LAMBDA=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_simple_lambda") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_SIMPLE_LAMBDA=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_capturing_lambda") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_CAPTURING_LAMBDA=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_multi_capturing_lambda") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_MULTI_CAPTURING_LAMBDA=1" ]
+  deps = _deps
+}
+
+pw_executable("callable_size_custom_class") {
+  sources = [ "callable_size.cc" ]
+  defines = [ "_CUSTOM_CLASS=1" ]
+  deps = _deps
+}
diff --git a/pw_function/size_report/basic_function.cc b/pw_function/size_report/basic_function.cc
new file mode 100644
index 0000000..14f7ea9
--- /dev/null
+++ b/pw_function/size_report/basic_function.cc
@@ -0,0 +1,31 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_function/function.h"
+
+namespace {
+
+int volatile* unoptimizable;
+
+void DoTheThing(int value, pw::Function<void(int)> callback) {
+  callback(value ^ *unoptimizable);
+}
+
+}  // namespace
+
+int main() {
+  int result;
+  DoTheThing(3, [&](int r) { result = r; });
+  return result;
+}
diff --git a/pw_function/size_report/callable_size.cc b/pw_function/size_report/callable_size.cc
new file mode 100644
index 0000000..3b00b24
--- /dev/null
+++ b/pw_function/size_report/callable_size.cc
@@ -0,0 +1,73 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <array>
+#include <cstddef>
+
+namespace {
+
+int volatile* unoptimizable;
+
+template <typename Callable>
+class CallableSize {
+ public:
+  constexpr CallableSize(Callable callable) : callable_(std::move(callable)) {}
+
+  int PreventOptimization() { return *unoptimizable; }
+
+ private:
+  alignas(std::max_align_t) Callable callable_;
+};
+
+[[maybe_unused]] void Function() {}
+
+class CustomCallableClass {
+ public:
+  void operator()() {}
+
+ private:
+  std::array<std::byte, 16> data_;
+};
+
+}  // namespace
+
+int main() {
+  int a = 0;
+  int b = 1;
+  int c = 2;
+  int d = 3;
+  static_cast<void>(a);
+  static_cast<void>(b);
+  static_cast<void>(c);
+  static_cast<void>(d);
+
+#if defined(_BASE)
+  CallableSize<std::array<std::byte, 0>> callable_size({});
+#elif defined(_FUNCTION_POINTER)
+  static CallableSize callable_size(Function);
+#elif defined(_STATIC_LAMBDA)
+  static CallableSize callable_size(+[]() {});
+#elif defined(_SIMPLE_LAMBDA)
+  static CallableSize callable_size([]() {});
+#elif defined(_CAPTURING_LAMBDA)
+  static CallableSize callable_size([a]() {});
+#elif defined(_MULTI_CAPTURING_LAMBDA)
+  static CallableSize callable_size([a, b, c, d]() {});
+#elif defined(_CUSTOM_CLASS)
+  static CallableSize callable_size((CustomCallableClass()));
+#endif
+
+  int foo = callable_size.PreventOptimization();
+  return sizeof(callable_size) + foo;
+}
diff --git a/pw_function/size_report/pointer_base.cc b/pw_function/size_report/pointer_base.cc
new file mode 100644
index 0000000..581d6ce
--- /dev/null
+++ b/pw_function/size_report/pointer_base.cc
@@ -0,0 +1,32 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+namespace {
+
+int volatile* unoptimizable;
+
+void DoTheThing(int value, void (*callback)(int)) {
+  callback(value ^ *unoptimizable);
+}
+
+int thing_result;
+
+void Callback(int result) { thing_result = result; }
+
+}  // namespace
+
+int main() {
+  DoTheThing(3, Callback);
+  return thing_result;
+}