-- a9ac6567c0933d786d68c10011e3f3ff9deedf89 by Greg Falcon <gfalcon@google.com>: Add absl::FunctionRef, a type analogous to the proposed C++23 std::function_ref. Like std::function, FunctionRef can be used to type-erase any callable (invokable) object. However, FunctionRef works by reference: it does not store a copy of the type-erased object. If the wrapped object is destroyed before the FunctionRef, the reference becomes dangling. FunctionRef relates to std::function in much the same way that string_view relates to std::string. Because of these limitations, FunctionRef is best used only as a function argument type, and only where the function will be invoked immediately (rather than saved for later use). When `const std::function<...>&` is used in this way, `absl::FunctionRef<...>` is a better-performing replacement. PiperOrigin-RevId: 275484044 -- 1f7c4df3760f8b93e5a5baf40b070eca1d3f4c98 by Abseil Team <absl-team@google.com>: Add FastHexToBufferZeroPad16() function for blazingly fast hex encoding of uint64_t. PiperOrigin-RevId: 275420901 -- 08d48ac004eba57cf2f1ada827181a2995f74807 by Abseil Team <absl-team@google.com>: Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275366326 -- 40be82bd2b34670b5458c0a72a0475086153c2d6 by Abseil Team <absl-team@google.com>: Added comments to SimpleAtof()/SimpleAtod() that clarify that they always use the "C" locale, unlike the standard functions strtod() and strtof() referenced now in the comments. PiperOrigin-RevId: 275355815 -- 086779dacb3f6f2b3ab59947e94e79046bdb1fe1 by Jorg Brown <jorg@google.com>: Move the hex conversion table used by escaping.cc into numbers.h so that other parts of Abseil can more efficiently access it. PiperOrigin-RevId: 275331251 -- 3c4ed1b04e55d96a40cbe70fb70929ffbb0c0432 by Abseil Team <absl-team@google.com>: Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275323858 -- 56ceb58ab688c3761978308609b09a1ac2739c9a by Derek Mauro <dmauro@google.com>: Add script for testing on Alpine Linux (for musl test coverage) PiperOrigin-RevId: 275321244 GitOrigin-RevId: a9ac6567c0933d786d68c10011e3f3ff9deedf89 Change-Id: I39799fa03768ddb44f3166200c860e1da4461807pull/393/head
parent
a15364ce4d
commit
e4c8d0eb8e
15 changed files with 887 additions and 40 deletions
@ -0,0 +1,51 @@ |
|||||||
|
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") |
||||||
|
load( |
||||||
|
"//absl:copts/configure_copts.bzl", |
||||||
|
"ABSL_DEFAULT_COPTS", |
||||||
|
"ABSL_DEFAULT_LINKOPTS", |
||||||
|
"ABSL_TEST_COPTS", |
||||||
|
) |
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"]) |
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0 |
||||||
|
|
||||||
|
cc_library( |
||||||
|
name = "function_ref", |
||||||
|
srcs = ["internal/function_ref.h"], |
||||||
|
hdrs = ["function_ref.h"], |
||||||
|
copts = ABSL_DEFAULT_COPTS, |
||||||
|
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||||
|
deps = [ |
||||||
|
"//absl/base:base_internal", |
||||||
|
"//absl/meta:type_traits", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_test( |
||||||
|
name = "function_ref_test", |
||||||
|
size = "small", |
||||||
|
srcs = ["function_ref_test.cc"], |
||||||
|
copts = ABSL_TEST_COPTS, |
||||||
|
deps = [ |
||||||
|
":function_ref", |
||||||
|
"//absl/container:test_instance_tracker", |
||||||
|
"//absl/memory", |
||||||
|
"@com_google_googletest//:gtest_main", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_test( |
||||||
|
name = "function_ref_benchmark", |
||||||
|
srcs = [ |
||||||
|
"function_ref_benchmark.cc", |
||||||
|
], |
||||||
|
copts = ABSL_TEST_COPTS, |
||||||
|
tags = ["benchmark"], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":function_ref", |
||||||
|
"//absl/base:core_headers", |
||||||
|
"@com_github_google_benchmark//:benchmark_main", |
||||||
|
], |
||||||
|
) |
@ -0,0 +1,137 @@ |
|||||||
|
// Copyright 2019 The Abseil 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.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// File: function_ref.h
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// This header file defines the `absl::FunctionRef` type for holding a
|
||||||
|
// non-owning reference to an object of any invocable type. This function
|
||||||
|
// reference is typically most useful as a type-erased argument type for
|
||||||
|
// accepting function types that neither take ownership nor copy the type; using
|
||||||
|
// the reference type in this case avoids a copy and an allocation. Best
|
||||||
|
// practices of other non-owning reference-like objects (such as
|
||||||
|
// `absl::string_view`) apply here.
|
||||||
|
//
|
||||||
|
// An `absl::FunctionRef` is similar in usage to a `std::function` but has the
|
||||||
|
// following differences:
|
||||||
|
//
|
||||||
|
// * It doesn't own the underlying object.
|
||||||
|
// * It doesn't have a null or empty state.
|
||||||
|
// * It never performs deep copies or allocations.
|
||||||
|
// * It's much faster and cheaper to construct.
|
||||||
|
// * It's trivially copyable and destructable.
|
||||||
|
//
|
||||||
|
// Generally, `absl::FunctionRef` should not be used as a return value, data
|
||||||
|
// member, or to initialize a `std::function`. Such usages will often lead to
|
||||||
|
// problematic lifetime issues. Once you convert something to an
|
||||||
|
// `absl::FunctionRef` you cannot make a deep copy later.
|
||||||
|
//
|
||||||
|
// This class is suitable for use wherever a "const std::function<>&"
|
||||||
|
// would be used without making a copy. ForEach functions and other versions of
|
||||||
|
// the visitor pattern are a good example of when this class should be used.
|
||||||
|
//
|
||||||
|
// This class is trivial to copy and should be passed by value.
|
||||||
|
#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_ |
||||||
|
#define ABSL_FUNCTIONAL_FUNCTION_REF_H_ |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <functional> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include "absl/functional/internal/function_ref.h" |
||||||
|
#include "absl/meta/type_traits.h" |
||||||
|
|
||||||
|
namespace absl { |
||||||
|
|
||||||
|
// FunctionRef
|
||||||
|
//
|
||||||
|
// Dummy class declaration to allow the partial specialization based on function
|
||||||
|
// types below.
|
||||||
|
template <typename T> |
||||||
|
class FunctionRef; |
||||||
|
|
||||||
|
// FunctionRef
|
||||||
|
//
|
||||||
|
// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with
|
||||||
|
// a compatible signature. Generally, an `absl::FunctionRef` should only be used
|
||||||
|
// as an argument type and should be preferred as an argument over a const
|
||||||
|
// reference to a `std::function`.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // The following function takes a function callback by const reference
|
||||||
|
// bool Visitor(const std::function<void(my_proto&,
|
||||||
|
// absl::string_view)>& callback);
|
||||||
|
//
|
||||||
|
// // Assuming that the function is not stored or otherwise copied, it can be
|
||||||
|
// // replaced by an `absl::FunctionRef`:
|
||||||
|
// bool Visitor(absl::FunctionRef<void(my_proto&, absl::string_view)>
|
||||||
|
// callback);
|
||||||
|
//
|
||||||
|
// Note: the assignment operator within an `absl::FunctionRef` is intentionally
|
||||||
|
// deleted to prevent misuse; because the `absl::FunctionRef` does not own the
|
||||||
|
// underlying type, assignment likely indicates misuse.
|
||||||
|
template <typename R, typename... Args> |
||||||
|
class FunctionRef<R(Args...)> { |
||||||
|
private: |
||||||
|
// Used to disable constructors for objects that are not compatible with the
|
||||||
|
// signature of this FunctionRef.
|
||||||
|
template <typename F, |
||||||
|
typename FR = absl::base_internal::InvokeT<F, Args&&...>> |
||||||
|
using EnableIfCompatible = |
||||||
|
typename std::enable_if<std::is_void<R>::value || |
||||||
|
std::is_convertible<FR, R>::value>::type; |
||||||
|
|
||||||
|
public: |
||||||
|
// Constructs a FunctionRef from any invokable type.
|
||||||
|
template <typename F, typename = EnableIfCompatible<const F&>> |
||||||
|
FunctionRef(const F& f) // NOLINT(runtime/explicit)
|
||||||
|
: invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) { |
||||||
|
absl::functional_internal::AssertNonNull(f); |
||||||
|
ptr_.obj = &f; |
||||||
|
} |
||||||
|
|
||||||
|
// Overload for function pointers. This eliminates a level of indirection that
|
||||||
|
// would happen if the above overload was used (it lets us store the pointer
|
||||||
|
// instead of a pointer to a pointer).
|
||||||
|
//
|
||||||
|
// This overload is also used for references to functions, since references to
|
||||||
|
// functions can decay to function pointers implicitly.
|
||||||
|
template < |
||||||
|
typename F, typename = EnableIfCompatible<F*>, |
||||||
|
absl::functional_internal::EnableIf<absl::is_function<F>::value> = 0> |
||||||
|
FunctionRef(F* f) // NOLINT(runtime/explicit)
|
||||||
|
: invoker_(&absl::functional_internal::InvokeFunction<F*, R, Args...>) { |
||||||
|
assert(f != nullptr); |
||||||
|
ptr_.fun = reinterpret_cast<decltype(ptr_.fun)>(f); |
||||||
|
} |
||||||
|
|
||||||
|
// To help prevent subtle lifetime bugs, FunctionRef is not assignable.
|
||||||
|
// Typically, it should only be used as an argument type.
|
||||||
|
FunctionRef& operator=(const FunctionRef& rhs) = delete; |
||||||
|
|
||||||
|
// Call the underlying object.
|
||||||
|
R operator()(Args... args) const { |
||||||
|
return invoker_(ptr_, std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
absl::functional_internal::VoidPtr ptr_; |
||||||
|
absl::functional_internal::Invoker<R, Args...> invoker_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace absl
|
||||||
|
|
||||||
|
#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_
|
@ -0,0 +1,140 @@ |
|||||||
|
// Copyright 2019 The Abseil 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 "absl/functional/function_ref.h" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "benchmark/benchmark.h" |
||||||
|
#include "absl/base/attributes.h" |
||||||
|
|
||||||
|
namespace absl { |
||||||
|
namespace { |
||||||
|
|
||||||
|
int dummy = 0; |
||||||
|
|
||||||
|
void FreeFunction() { benchmark::DoNotOptimize(dummy); } |
||||||
|
|
||||||
|
struct TrivialFunctor { |
||||||
|
void operator()() const { benchmark::DoNotOptimize(dummy); } |
||||||
|
}; |
||||||
|
|
||||||
|
struct LargeFunctor { |
||||||
|
void operator()() const { benchmark::DoNotOptimize(this); } |
||||||
|
std::string a, b, c; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Function, typename... Args> |
||||||
|
void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) { |
||||||
|
f(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Function, typename Callable, typename... Args> |
||||||
|
void ConstructAndCallFunctionBenchmark(benchmark::State& state, |
||||||
|
const Callable& c, Args&&... args) { |
||||||
|
for (auto _ : state) { |
||||||
|
CallFunction<Function>(c, std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void BM_TrivialStdFunction(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<std::function<void()>>(state, |
||||||
|
TrivialFunctor{}); |
||||||
|
} |
||||||
|
BENCHMARK(BM_TrivialStdFunction); |
||||||
|
|
||||||
|
void BM_TrivialFunctionRef(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, |
||||||
|
TrivialFunctor{}); |
||||||
|
} |
||||||
|
BENCHMARK(BM_TrivialFunctionRef); |
||||||
|
|
||||||
|
void BM_LargeStdFunction(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<std::function<void()>>(state, |
||||||
|
LargeFunctor{}); |
||||||
|
} |
||||||
|
BENCHMARK(BM_LargeStdFunction); |
||||||
|
|
||||||
|
void BM_LargeFunctionRef(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, LargeFunctor{}); |
||||||
|
} |
||||||
|
BENCHMARK(BM_LargeFunctionRef); |
||||||
|
|
||||||
|
void BM_FunPtrStdFunction(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<std::function<void()>>(state, FreeFunction); |
||||||
|
} |
||||||
|
BENCHMARK(BM_FunPtrStdFunction); |
||||||
|
|
||||||
|
void BM_FunPtrFunctionRef(benchmark::State& state) { |
||||||
|
ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, FreeFunction); |
||||||
|
} |
||||||
|
BENCHMARK(BM_FunPtrFunctionRef); |
||||||
|
|
||||||
|
// Doesn't include construction or copy overhead in the loop.
|
||||||
|
template <typename Function, typename Callable, typename... Args> |
||||||
|
void CallFunctionBenchmark(benchmark::State& state, const Callable& c, |
||||||
|
Args... args) { |
||||||
|
Function f = c; |
||||||
|
for (auto _ : state) { |
||||||
|
benchmark::DoNotOptimize(&f); |
||||||
|
f(args...); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct FunctorWithTrivialArgs { |
||||||
|
void operator()(int a, int b, int c) const { |
||||||
|
benchmark::DoNotOptimize(a); |
||||||
|
benchmark::DoNotOptimize(b); |
||||||
|
benchmark::DoNotOptimize(c); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void BM_TrivialArgsStdFunction(benchmark::State& state) { |
||||||
|
CallFunctionBenchmark<std::function<void(int, int, int)>>( |
||||||
|
state, FunctorWithTrivialArgs{}, 1, 2, 3); |
||||||
|
} |
||||||
|
BENCHMARK(BM_TrivialArgsStdFunction); |
||||||
|
|
||||||
|
void BM_TrivialArgsFunctionRef(benchmark::State& state) { |
||||||
|
CallFunctionBenchmark<FunctionRef<void(int, int, int)>>( |
||||||
|
state, FunctorWithTrivialArgs{}, 1, 2, 3); |
||||||
|
} |
||||||
|
BENCHMARK(BM_TrivialArgsFunctionRef); |
||||||
|
|
||||||
|
struct FunctorWithNonTrivialArgs { |
||||||
|
void operator()(std::string a, std::string b, std::string c) const { |
||||||
|
benchmark::DoNotOptimize(&a); |
||||||
|
benchmark::DoNotOptimize(&b); |
||||||
|
benchmark::DoNotOptimize(&c); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void BM_NonTrivialArgsStdFunction(benchmark::State& state) { |
||||||
|
std::string a, b, c; |
||||||
|
CallFunctionBenchmark< |
||||||
|
std::function<void(std::string, std::string, std::string)>>( |
||||||
|
state, FunctorWithNonTrivialArgs{}, a, b, c); |
||||||
|
} |
||||||
|
BENCHMARK(BM_NonTrivialArgsStdFunction); |
||||||
|
|
||||||
|
void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { |
||||||
|
std::string a, b, c; |
||||||
|
CallFunctionBenchmark< |
||||||
|
FunctionRef<void(std::string, std::string, std::string)>>( |
||||||
|
state, FunctorWithNonTrivialArgs{}, a, b, c); |
||||||
|
} |
||||||
|
BENCHMARK(BM_NonTrivialArgsFunctionRef); |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace absl
|
@ -0,0 +1,255 @@ |
|||||||
|
// Copyright 2019 The Abseil 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 "absl/functional/function_ref.h" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "gmock/gmock.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
#include "absl/container/internal/test_instance_tracker.h" |
||||||
|
#include "absl/memory/memory.h" |
||||||
|
|
||||||
|
namespace absl { |
||||||
|
namespace { |
||||||
|
|
||||||
|
void RunFun(FunctionRef<void()> f) { f(); } |
||||||
|
|
||||||
|
TEST(FunctionRefTest, Lambda) { |
||||||
|
bool ran = false; |
||||||
|
RunFun([&] { ran = true; }); |
||||||
|
EXPECT_TRUE(ran); |
||||||
|
} |
||||||
|
|
||||||
|
int Function() { return 1337; } |
||||||
|
|
||||||
|
TEST(FunctionRefTest, Function1) { |
||||||
|
FunctionRef<int()> ref(&Function); |
||||||
|
EXPECT_EQ(1337, ref()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRefTest, Function2) { |
||||||
|
FunctionRef<int()> ref(Function); |
||||||
|
EXPECT_EQ(1337, ref()); |
||||||
|
} |
||||||
|
|
||||||
|
int NoExceptFunction() noexcept { return 1337; } |
||||||
|
|
||||||
|
// TODO(jdennett): Add a test for noexcept member functions.
|
||||||
|
TEST(FunctionRefTest, NoExceptFunction) { |
||||||
|
FunctionRef<int()> ref(NoExceptFunction); |
||||||
|
EXPECT_EQ(1337, ref()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRefTest, ForwardsArgs) { |
||||||
|
auto l = [](std::unique_ptr<int> i) { return *i; }; |
||||||
|
FunctionRef<int(std::unique_ptr<int>)> ref(l); |
||||||
|
EXPECT_EQ(42, ref(absl::make_unique<int>(42))); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, ReturnMoveOnly) { |
||||||
|
auto l = [] { return absl::make_unique<int>(29); }; |
||||||
|
FunctionRef<std::unique_ptr<int>()> ref(l); |
||||||
|
EXPECT_EQ(29, *ref()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, ManyArgs) { |
||||||
|
auto l = [](int a, int b, int c) { return a + b + c; }; |
||||||
|
FunctionRef<int(int, int, int)> ref(l); |
||||||
|
EXPECT_EQ(6, ref(1, 2, 3)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, VoidResultFromNonVoidFunctor) { |
||||||
|
bool ran = false; |
||||||
|
auto l = [&]() -> int { |
||||||
|
ran = true; |
||||||
|
return 2; |
||||||
|
}; |
||||||
|
FunctionRef<void()> ref(l); |
||||||
|
ref(); |
||||||
|
EXPECT_TRUE(ran); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, CastFromDerived) { |
||||||
|
struct Base {}; |
||||||
|
struct Derived : public Base {}; |
||||||
|
|
||||||
|
Derived d; |
||||||
|
auto l1 = [&](Base* b) { EXPECT_EQ(&d, b); }; |
||||||
|
FunctionRef<void(Derived*)> ref1(l1); |
||||||
|
ref1(&d); |
||||||
|
|
||||||
|
auto l2 = [&]() -> Derived* { return &d; }; |
||||||
|
FunctionRef<Base*()> ref2(l2); |
||||||
|
EXPECT_EQ(&d, ref2()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, VoidResultFromNonVoidFuncton) { |
||||||
|
FunctionRef<void()> ref(Function); |
||||||
|
ref(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, MemberPtr) { |
||||||
|
struct S { |
||||||
|
int i; |
||||||
|
}; |
||||||
|
|
||||||
|
S s{1100111}; |
||||||
|
auto mem_ptr = &S::i; |
||||||
|
FunctionRef<int(const S& s)> ref(mem_ptr); |
||||||
|
EXPECT_EQ(1100111, ref(s)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, MemberFun) { |
||||||
|
struct S { |
||||||
|
int i; |
||||||
|
int get_i() const { return i; } |
||||||
|
}; |
||||||
|
|
||||||
|
S s{22}; |
||||||
|
auto mem_fun_ptr = &S::get_i; |
||||||
|
FunctionRef<int(const S& s)> ref(mem_fun_ptr); |
||||||
|
EXPECT_EQ(22, ref(s)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, MemberFunRefqualified) { |
||||||
|
struct S { |
||||||
|
int i; |
||||||
|
int get_i() && { return i; } |
||||||
|
}; |
||||||
|
auto mem_fun_ptr = &S::get_i; |
||||||
|
S s{22}; |
||||||
|
FunctionRef<int(S && s)> ref(mem_fun_ptr); |
||||||
|
EXPECT_EQ(22, ref(std::move(s))); |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(_WIN32) && defined(GTEST_HAS_DEATH_TEST) |
||||||
|
|
||||||
|
TEST(FunctionRef, MemberFunRefqualifiedNull) { |
||||||
|
struct S { |
||||||
|
int i; |
||||||
|
int get_i() && { return i; } |
||||||
|
}; |
||||||
|
auto mem_fun_ptr = &S::get_i; |
||||||
|
mem_fun_ptr = nullptr; |
||||||
|
EXPECT_DEBUG_DEATH({ FunctionRef<int(S && s)> ref(mem_fun_ptr); }, ""); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, NullMemberPtrAssertFails) { |
||||||
|
struct S { |
||||||
|
int i; |
||||||
|
}; |
||||||
|
using MemberPtr = int S::*; |
||||||
|
MemberPtr mem_ptr = nullptr; |
||||||
|
EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, ""); |
||||||
|
} |
||||||
|
|
||||||
|
#endif // GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
|
TEST(FunctionRef, CopiesAndMovesPerPassByValue) { |
||||||
|
absl::test_internal::InstanceTracker tracker; |
||||||
|
absl::test_internal::CopyableMovableInstance instance(0); |
||||||
|
auto l = [](absl::test_internal::CopyableMovableInstance) {}; |
||||||
|
FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); |
||||||
|
ref(instance); |
||||||
|
EXPECT_EQ(tracker.copies(), 1); |
||||||
|
EXPECT_EQ(tracker.moves(), 1); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, CopiesAndMovesPerPassByRef) { |
||||||
|
absl::test_internal::InstanceTracker tracker; |
||||||
|
absl::test_internal::CopyableMovableInstance instance(0); |
||||||
|
auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; |
||||||
|
FunctionRef<void(const absl::test_internal::CopyableMovableInstance&)> ref(l); |
||||||
|
ref(instance); |
||||||
|
EXPECT_EQ(tracker.copies(), 0); |
||||||
|
EXPECT_EQ(tracker.moves(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, CopiesAndMovesPerPassByValueCallByMove) { |
||||||
|
absl::test_internal::InstanceTracker tracker; |
||||||
|
absl::test_internal::CopyableMovableInstance instance(0); |
||||||
|
auto l = [](absl::test_internal::CopyableMovableInstance) {}; |
||||||
|
FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); |
||||||
|
ref(std::move(instance)); |
||||||
|
EXPECT_EQ(tracker.copies(), 0); |
||||||
|
EXPECT_EQ(tracker.moves(), 2); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, CopiesAndMovesPerPassByValueToRef) { |
||||||
|
absl::test_internal::InstanceTracker tracker; |
||||||
|
absl::test_internal::CopyableMovableInstance instance(0); |
||||||
|
auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; |
||||||
|
FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); |
||||||
|
ref(std::move(instance)); |
||||||
|
EXPECT_EQ(tracker.copies(), 0); |
||||||
|
EXPECT_EQ(tracker.moves(), 1); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FunctionRef, PassByValueTypes) { |
||||||
|
using absl::functional_internal::Invoker; |
||||||
|
using absl::functional_internal::VoidPtr; |
||||||
|
using absl::test_internal::CopyableMovableInstance; |
||||||
|
struct Trivial { |
||||||
|
void* p[2]; |
||||||
|
}; |
||||||
|
struct LargeTrivial { |
||||||
|
void* p[3]; |
||||||
|
}; |
||||||
|
|
||||||
|
static_assert(std::is_same<Invoker<void, int>, void (*)(VoidPtr, int)>::value, |
||||||
|
"Scalar types should be passed by value"); |
||||||
|
static_assert( |
||||||
|
std::is_same<Invoker<void, Trivial>, void (*)(VoidPtr, Trivial)>::value, |
||||||
|
"Small trivial types should be passed by value"); |
||||||
|
static_assert(std::is_same<Invoker<void, LargeTrivial>, |
||||||
|
void (*)(VoidPtr, LargeTrivial &&)>::value, |
||||||
|
"Large trivial types should be passed by rvalue reference"); |
||||||
|
static_assert( |
||||||
|
std::is_same<Invoker<void, CopyableMovableInstance>, |
||||||
|
void (*)(VoidPtr, CopyableMovableInstance &&)>::value, |
||||||
|
"Types with copy/move ctor should be passed by rvalue reference"); |
||||||
|
|
||||||
|
// References are passed as references.
|
||||||
|
static_assert( |
||||||
|
std::is_same<Invoker<void, int&>, void (*)(VoidPtr, int&)>::value, |
||||||
|
"Reference types should be preserved"); |
||||||
|
static_assert( |
||||||
|
std::is_same<Invoker<void, CopyableMovableInstance&>, |
||||||
|
void (*)(VoidPtr, CopyableMovableInstance&)>::value, |
||||||
|
"Reference types should be preserved"); |
||||||
|
static_assert( |
||||||
|
std::is_same<Invoker<void, CopyableMovableInstance&&>, |
||||||
|
void (*)(VoidPtr, CopyableMovableInstance &&)>::value, |
||||||
|
"Reference types should be preserved"); |
||||||
|
|
||||||
|
// Make sure the address of an object received by reference is the same as the
|
||||||
|
// addess of the object passed by the caller.
|
||||||
|
{ |
||||||
|
LargeTrivial obj; |
||||||
|
auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); }; |
||||||
|
absl::FunctionRef<void(LargeTrivial&)> ref(test); |
||||||
|
ref(obj); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
Trivial obj; |
||||||
|
auto test = [&obj](Trivial& input) { ASSERT_EQ(&input, &obj); }; |
||||||
|
absl::FunctionRef<void(Trivial&)> ref(test); |
||||||
|
ref(obj); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace absl
|
@ -0,0 +1,104 @@ |
|||||||
|
// Copyright 2019 The Abseil 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.
|
||||||
|
|
||||||
|
#ifndef ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ |
||||||
|
#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <functional> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include "absl/base/internal/invoke.h" |
||||||
|
#include "absl/meta/type_traits.h" |
||||||
|
|
||||||
|
namespace absl { |
||||||
|
namespace functional_internal { |
||||||
|
|
||||||
|
// Like a void* that can handle function pointers as well. The standard does not
|
||||||
|
// allow function pointers to round-trip through void*, but void(*)() is fine.
|
||||||
|
//
|
||||||
|
// Note: It's important that this class remains trivial and is the same size as
|
||||||
|
// a pointer, since this allows the compiler to perform tail-call optimizations
|
||||||
|
// when the underlying function is a callable object with a matching signature.
|
||||||
|
union VoidPtr { |
||||||
|
const void* obj; |
||||||
|
void (*fun)(); |
||||||
|
}; |
||||||
|
|
||||||
|
// Chooses the best type for passing T as an argument.
|
||||||
|
// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are
|
||||||
|
// passed by value.
|
||||||
|
template <typename T> |
||||||
|
constexpr bool PassByValue() { |
||||||
|
return !std::is_lvalue_reference<T>::value && |
||||||
|
absl::is_trivially_copy_constructible<T>::value && |
||||||
|
absl::is_trivially_copy_assignable< |
||||||
|
typename std::remove_cv<T>::type>::value && |
||||||
|
std::is_trivially_destructible<T>::value && |
||||||
|
sizeof(T) <= 2 * sizeof(void*); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {}; |
||||||
|
|
||||||
|
// An Invoker takes a pointer to the type-erased invokable object, followed by
|
||||||
|
// the arguments that the invokable object expects.
|
||||||
|
//
|
||||||
|
// Note: The order of arguments here is an optimization, since member functions
|
||||||
|
// have an implicit "this" pointer as their first argument, putting VoidPtr
|
||||||
|
// first allows the compiler to perform tail-call optimization in many cases.
|
||||||
|
template <typename R, typename... Args> |
||||||
|
using Invoker = R (*)(VoidPtr, typename ForwardT<Args>::type...); |
||||||
|
|
||||||
|
//
|
||||||
|
// InvokeObject and InvokeFunction provide static "Invoke" functions that can be
|
||||||
|
// used as Invokers for objects or functions respectively.
|
||||||
|
//
|
||||||
|
// static_cast<R> handles the case the return type is void.
|
||||||
|
template <typename Obj, typename R, typename... Args> |
||||||
|
R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) { |
||||||
|
auto o = static_cast<const Obj*>(ptr.obj); |
||||||
|
return static_cast<R>( |
||||||
|
absl::base_internal::Invoke(*o, std::forward<Args>(args)...)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Fun, typename R, typename... Args> |
||||||
|
R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) { |
||||||
|
auto f = reinterpret_cast<Fun>(ptr.fun); |
||||||
|
return static_cast<R>( |
||||||
|
absl::base_internal::Invoke(f, std::forward<Args>(args)...)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Sig> |
||||||
|
void AssertNonNull(const std::function<Sig>& f) { |
||||||
|
assert(f != nullptr); |
||||||
|
(void)f; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename F> |
||||||
|
void AssertNonNull(const F&) {} |
||||||
|
|
||||||
|
template <typename F, typename C> |
||||||
|
void AssertNonNull(F C::*f) { |
||||||
|
assert(f != nullptr); |
||||||
|
(void)f; |
||||||
|
} |
||||||
|
|
||||||
|
template <bool C> |
||||||
|
using EnableIf = typename ::std::enable_if<C, int>::type; |
||||||
|
|
||||||
|
} // namespace functional_internal
|
||||||
|
} // namespace absl
|
||||||
|
|
||||||
|
#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
|
@ -0,0 +1,63 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# |
||||||
|
# Copyright 2019 The Abseil 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. |
||||||
|
|
||||||
|
# TODO(absl-team): This script isn't fully hermetic because |
||||||
|
# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed |
||||||
|
# version of GoogleTest. This means that an upstream change to GoogleTest could |
||||||
|
# break this test. Fix this by allowing this script to pin to a known-good |
||||||
|
# version of GoogleTest. |
||||||
|
|
||||||
|
set -euox pipefail |
||||||
|
|
||||||
|
if [ -z ${ABSEIL_ROOT:-} ]; then |
||||||
|
ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]; then |
||||||
|
ABSL_CMAKE_CXX_STANDARDS="11 14 17" |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]; then |
||||||
|
ABSL_CMAKE_BUILD_TYPES="Debug Release" |
||||||
|
fi |
||||||
|
|
||||||
|
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" |
||||||
|
|
||||||
|
for std in ${ABSL_CMAKE_CXX_STANDARDS}; do |
||||||
|
for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do |
||||||
|
echo "--------------------------------------------------------------------" |
||||||
|
echo "Testing with CMAKE_BUILD_TYPE=${compilation_mode} and -std=c++${std}" |
||||||
|
|
||||||
|
time docker run \ |
||||||
|
--volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ |
||||||
|
--workdir=/abseil-cpp \ |
||||||
|
--tmpfs=/buildfs:exec \ |
||||||
|
--cap-add=SYS_PTRACE \ |
||||||
|
--rm \ |
||||||
|
-e CFLAGS="-Werror" \ |
||||||
|
-e CXXFLAGS="-Werror" \ |
||||||
|
"${DOCKER_CONTAINER}" \ |
||||||
|
/bin/sh -c " |
||||||
|
cd /buildfs && \ |
||||||
|
cmake /abseil-cpp \ |
||||||
|
-DABSL_USE_GOOGLETEST_HEAD=ON \ |
||||||
|
-DABSL_RUN_TESTS=ON \ |
||||||
|
-DCMAKE_BUILD_TYPE=${compilation_mode} \ |
||||||
|
-DCMAKE_CXX_STANDARD=${std} && \ |
||||||
|
make -j$(nproc) && \ |
||||||
|
ctest -j$(nproc) --output-on-failure" |
||||||
|
done |
||||||
|
done |
Loading…
Reference in new issue