libfit Source: https://fuchsia.googlesource.com/fuchsia/+/main/sdk/lib/fit/ Version: 36303cd2d1611cb1b670235692d01a92e83ecd21 License: Copyright 2019 The Fuchsia Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====== FIT is a lean library of portable C++ abstractions for control flow and memory management beyond what is offered by the C++ 17 standard library. FIT only depends on the C++ language and standard library, including some C++17 library features. It offers essential enhancements to the C++ standard library rather than attempting to replace it or become a framework for writing applications. FIT can be thought of as an "annex" that expresses a few ideas we wish the C++ standard library might itself implement someday. FIT is lean. ## What Belongs in FIT Several Fuchsia SDK libraries, such as *libfidl*, depend on FIT and on the C++ standard library. As these libraries are broadly used, we must take care in deciding what features to include in FIT to avoid burdening developers with unnecessary code or dependencies. In general, the goal is to identify specific abstractions that make sense to generalize across the entire ecosystem of Fuchsia C++ applications. These will necessarily be somewhat low-level but high impact. We don't want to add code to FIT simply because we think it's cool. We need evidence that it is a common idiom and that a broad audience of developers will significantly benefit from its promotion. Here are a few criteria to consider: - Is the feature lightweight, general-purpose, and platform-independent? - Is the feature not well served by other means, particularly by the C++ standard library? - Is the feature needed by a Fuchsia SDK library? - Does the feature embody a beneficial idiom that clients of the Fuchsia SDK commonly use? - Has the feature been re-implemented many times already leading to code fragmentation that we would like to eliminate? If in doubt, leave it out. See [Justifications] below. ## What Doesn't Belong in FIT FIT is not intended to become a catch-all class library. Specifically prohibited features: - Features that introduce dependencies on libraries other than the C and C++ standard library. - Features that only work on certain operating systems. - Collection classes where the C++ 17 standard library already offers an adequate (if not perfect) alternative. - Classes that impose an implementation burden on clients such as event loops, dispatchers, frameworks, and other glue code. ## Implementation Considerations FIT is not exception safe (but could be made to be in the future). ## Style Conventions The API style was modified to fit current android::base library conventions. In brief: - Class identifiers are CamelCase - Class methods and variable identifiers use "camelCase", class fields use "mCamelCase". - Template parameters are `CamelCase`. - Preprocessor macros are `UPPER_SNAKE_CASE`. ## Justifications These sections explain why certain features are in FIT. ### fit::Function - *libfidl*'s API needs a callable function wrapper with move semantics but C++ 14's `std::function` only supports copyable function objects which forces FIDL to allocate callback state on the heap making programs less efficient and harder to write. - Lots of other C++ code uses callbacks extensively and would benefit from move semantics for similar reasons. - So we should create a move-only function wrapper to use everywhere. ### fit::Defer - When writing asynchronous event-driven programs, it can become challenging to ensure that resources remain in scope for the duration of an operation in progress and are subsequently released. - The C++ 14 standard library offers several classes with RAII semantics, such as `std::unique_ptr`, which are helpful in these situations. Unfortunately the C++ 14 standard library does not offer affordances for easily invoking a function when a block or object goes out of scope short of implementing a new class from scratch. - We have observed several re-implementations of the same idea throughout the system. - So we should create a simple way to invoke a function on scope exit. ### fit::Nullable - Case study: fit::defer has a need to store a closure that may be nullable. We were able to replace its hand-rolled lifetime management code with fit::nullable thereby vastly simplifying its implementation. - Case study: fit::future has a need to track its own validity along with a continuation that may or not be present. - Case study: We have previously observed bugs where developers were surprised when assigning a null closure to wrappers such as fit::function fit::defer, or fit::future left these objects in a supposedly "valid" but uninvocable state. These objects therefore take care to detect null closures and enter an "invalid" state. Using fit::is_null and fit::nullable makes it easier to eliminate this redundant state and simplifies the API for clients of these wrappers. - std::optional can be effective here but it doesn't directly handle nullity so it takes more care to coalesce the null and "not present" states. std::optional also increases the size of the object to carry an extra bool and passing, whereas fit::nullable eliminates this overhead by taking advantage of the underlying value's null state (if there is one). - So we introduce fit::nullable to handle both cases systematically while still hewing close to the semantics of std::optional.