BLI: improve FunctionRef constructor in case of a falsy callable

Previously, passing a falsy `std::function` (i.e. it is empty) or a null
function pointer would result in a `FunctionRef` which is thruthy and thus
appeared callable. Now, when the passed in callable is falsy, the resulting
`FunctionRef` will be too.

Pull Request: https://projects.blender.org/blender/blender/pulls/128823
This commit is contained in:
Jacques Lucke
2024-10-10 03:14:44 +02:00
parent 0cec2462cd
commit b79dd3fc5d
2 changed files with 47 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
#include <type_traits>
#include <utility>
#include "BLI_build_config.h"
#include "BLI_utildefines.h"
/** \file
@@ -119,6 +120,26 @@ template<typename Ret, typename... Params> class FunctionRef<Ret(Params...)> {
: 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"
# pragma GCC diagnostic ignored "-Wnonnull-compare"
#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
}
}
/**

View File

@@ -126,4 +126,30 @@ TEST(function_ref, OverloadSelection)
EXPECT_EQ(overload_test(fn_2), 2);
}
TEST(function_ref, FalsyStdFunction)
{
const std::function<void()> fn1;
const std::function<void()> fn2 = [&]() {};
const FunctionRef<void()> fn_ref1(fn1);
const FunctionRef<void()> fn_ref2(fn2);
EXPECT_FALSE(fn_ref1);
EXPECT_TRUE(fn_ref2);
const FunctionRef<void()> fn_ref3(fn1);
const FunctionRef<void()> fn_ref4(fn2);
EXPECT_FALSE(fn_ref3);
EXPECT_TRUE(fn_ref4);
}
TEST(function_ref, FalsyFunctionPointer)
{
using Fn = void (*)();
const Fn fn1 = nullptr;
const Fn fn2 = []() {};
const FunctionRef<void()> fn_ref1(fn1);
const FunctionRef<void()> fn_ref2(fn2);
EXPECT_FALSE(fn_ref1);
EXPECT_TRUE(fn_ref2);
}
} // namespace blender::tests