Files
test2/source/blender/blenlib/BLI_function_ref.hh
Brecht Van Lommel 920e709069 Refactor: Make header files more clangd and clang-tidy friendly
When using clangd or running clang-tidy on headers there are
currently many errors. These are noisy in IDEs, make auto fixes
impossible, and break features like code completion, refactoring
and navigation.

This makes source/blender headers work by themselves, which is
generally the goal anyway. But #includes and forward declarations
were often incomplete.

* Add #includes and forward declarations
* Add IWYU pragma: export in a few places
* Remove some unused #includes (but there are many more)
* Tweak ShaderCreateInfo macros to work better with clangd

Some types of headers still have errors, these could be fixed or
worked around with more investigation. Mostly preprocessor
template headers like NOD_static_types.h.

Note that that disabling WITH_UNITY_BUILD is required for clangd to
work properly, otherwise compile_commands.json does not contain
the information for the relevant source files.

For more details see the developer docs:
https://developer.blender.org/docs/handbook/tooling/clangd/

Pull Request: https://projects.blender.org/blender/blender/pulls/132608
2025-01-07 12:39:13 +01:00

167 lines
6.3 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <type_traits>
#include <utility>
#include "BLI_build_config.h"
#include "BLI_utildefines.h"
/** \file
* \ingroup bli
*
* A `FunctionRef<Signature>` is a non-owning reference to some callable object with a specific
* signature. It can be used to pass some callback to another function.
*
* A `FunctionRef` is small and cheap to copy. Therefore it should generally be passed by value.
*
* Example signatures:
* `FunctionRef<void()>` - A function without parameters and void return type.
* `FunctionRef<int(float)>` - A function with a float parameter and an int return value.
* `FunctionRef<int(int, int)>` - A function with two int parameters and an int return value.
*
* There are multiple ways to achieve that, so here is a comparison of the different approaches:
* 1. Pass function pointer and user data (as void *) separately:
* - The only method that is compatible with C interfaces.
* - Is cumbersome to work with in many cases, because one has to keep track of two parameters.
* - Not type safe at all, because of the void pointer.
* - It requires workarounds when one wants to pass a lambda into a function.
* 2. Using `std::function`:
* - It works well with most callables and is easy to use.
* - Owns the callable, so it can be returned from a function more safely than other methods.
* - Requires that the callable is copyable.
* - Requires an allocation when the callable is too large (typically > 16 bytes).
* 3. Using a template for the callable type:
* - Most efficient solution at runtime, because compiler knows the exact callable at the place
* where it is called.
* - Works well with all callables.
* - Requires the function to be in a header file.
* - It's difficult to constrain the signature of the function.
* 4. Using `FunctionRef`:
* - Second most efficient solution at runtime.
* - It's easy to constrain the signature of the callable.
* - Does not require the function to be in a header file.
* - Works well with all callables.
* - It's a non-owning reference, so it *cannot* be stored safely in general.
*
* The fact that this is a non-owning reference makes `FunctionRef` very well suited for some use
* cases, but one has to be a bit more careful when using it to make sure that the referenced
* callable is not destructed.
*
* In particular, one must not construct a `FunctionRef` variable from a lambda directly as shown
* below. This is because the lambda object goes out of scope after the line finished executing and
* will be destructed. Calling the reference afterwards invokes undefined behavior.
*
* Don't:
* FunctionRef<int()> ref = []() { return 0; };
* Do:
* auto f = []() { return 0; };
* FuntionRef<int()> ref = f;
*
* It is fine to pass a lambda directly to a function:
*
* void some_function(FunctionRef<int()> f);
* some_function([]() { return 0; });
*/
namespace blender {
template<typename Function> class FunctionRef;
template<typename Ret, typename... Params> class FunctionRef<Ret(Params...)> {
private:
/**
* A function pointer that knows how to call the referenced callable with the given parameters.
*/
Ret (*callback_)(intptr_t callable, Params... params) = nullptr;
/**
* A pointer to the referenced callable object. This can be a C function, a lambda object or any
* other callable.
*
* The value does not need to be initialized because it is not used unless `callback_` is set as
* well, in which case it will be initialized as well.
*
* Use `intptr_t` to avoid warnings when casting to function pointers.
*/
intptr_t callable_;
template<typename Callable> static Ret callback_fn(intptr_t callable, Params... params)
{
return (*reinterpret_cast<Callable *>(callable))(std::forward<Params>(params)...);
}
public:
FunctionRef() = default;
FunctionRef(std::nullptr_t) {}
/**
* A `FunctionRef` itself is a callable as well. However, we don't want that this
* constructor is called when `Callable` is a `FunctionRef`. If we would allow this, it
* would be easy to accidentally create a `FunctionRef` that internally calls another
* `FunctionRef`. Usually, when assigning a `FunctionRef` to another, we want that both
* contain a reference to the same underlying callable afterwards.
*
* It is still possible to reference another `FunctionRef` by first wrapping it in
* another lambda.
*/
template<typename Callable,
BLI_ENABLE_IF((
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Callable>>, FunctionRef>)),
BLI_ENABLE_IF((std::is_invocable_r_v<Ret, Callable, Params...>))>
FunctionRef(Callable &&callable)
: callback_(callback_fn<typename std::remove_reference_t<Callable>>),
callable_(intptr_t(&callable))
{
if constexpr (std::is_constructible_v<bool, Callable>) {
/* For some types, the compiler can be sure that the callable is always truthy. Good!
* Then the entire check can be optimized away. */
#if COMPILER_CLANG || COMPILER_GCC
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Waddress"
# if COMPILER_GCC
# pragma GCC diagnostic ignored "-Wnonnull-compare"
# endif
#endif
/* Make sure the #FunctionRef is falsy if the callback is falsy.
* That can happen when passing in null or empty std::function. */
const bool is_truthy = bool(callable);
if (!is_truthy) {
callback_ = nullptr;
callable_ = 0;
}
#if COMPILER_CLANG || COMPILER_GCC
# pragma GCC diagnostic pop
#endif
}
}
/**
* Call the referenced function and forward all parameters to it.
*
* This invokes undefined behavior if the `FunctionRef` does not reference a function currently.
*/
Ret operator()(Params... params) const
{
BLI_assert(callback_ != nullptr);
return callback_(callable_, std::forward<Params>(params)...);
}
/**
* Returns true, when the `FunctionRef` references a function currently.
* If this returns false, the `FunctionRef` must not be called.
*/
operator bool() const
{
/* Just checking `callback_` is enough to determine if the `FunctionRef` is in a state that it
* can be called in. */
return callback_ != nullptr;
}
};
} // namespace blender