-- 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