diff --git a/api/include/opentelemetry/nostd/function_ref.h b/api/include/opentelemetry/nostd/function_ref.h new file mode 100644 index 00000000000..33c94d00ab2 --- /dev/null +++ b/api/include/opentelemetry/nostd/function_ref.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +template +class function_ref; + +/** + * Non-owning function reference that can be used as a more performant + * replacement for std::function when ownership sematics aren't needed. + * + * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0792r0.html + * + * Based off of https://stackoverflow.com/a/39087660/4447365 + */ +template +class function_ref +{ + void *callable_ = nullptr; + R (*invoker_)(void *, Args...) = nullptr; + + template + using FunctionPointer = decltype(std::addressof(std::declval())); + + template + void BindTo(F &f) noexcept + { + callable_ = static_cast(std::addressof(f)); + invoker_ = [](void *callable_, Args... args) -> R { + return (*static_cast>(callable_))(std::forward(args)...); + }; + } + + template + void BindTo(R_in (*f)(Args_in...)) noexcept + { + using F = decltype(f); + if (f == nullptr) + { + return BindTo(nullptr); + } + callable_ = reinterpret_cast(f); + invoker_ = [](void *callable_, Args... args) -> R { + return (F(callable_))(std::forward(args)...); + }; + } + + void BindTo(std::nullptr_t) noexcept + { + callable_ = nullptr; + invoker_ = nullptr; + } + +public: + template < + class F, + typename std::enable_if::type>::value, + int>::type = 0, + typename std::enable_if< + std::is_convertible::type, R>::value, + int>::type = 0> + function_ref(F &&f) + { + BindTo(f); // not forward + } + + function_ref(std::nullptr_t) {} + + function_ref(const function_ref &) noexcept = default; + function_ref(function_ref &&) noexcept = default; + + R operator()(Args... args) const { return invoker_(callable_, std::forward(args)...); } + + explicit operator bool() const { return invoker_; } +}; +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/nostd/BUILD b/api/test/nostd/BUILD index c3f2d6864f9..c98513f5ef7 100644 --- a/api/test/nostd/BUILD +++ b/api/test/nostd/BUILD @@ -1,3 +1,14 @@ +cc_test( + name = "function_ref_test", + srcs = [ + "function_ref_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "string_view_test", srcs = [ diff --git a/api/test/nostd/CMakeLists.txt b/api/test/nostd/CMakeLists.txt index 60d833f0cda..fdbf8e7b072 100644 --- a/api/test/nostd/CMakeLists.txt +++ b/api/test/nostd/CMakeLists.txt @@ -1,7 +1,7 @@ include(GoogleTest) -foreach(testname string_view_test unique_ptr_test utility_test span_test - shared_ptr_test) +foreach(testname function_ref_test string_view_test unique_ptr_test + utility_test span_test shared_ptr_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) diff --git a/api/test/nostd/function_ref_test.cc b/api/test/nostd/function_ref_test.cc new file mode 100644 index 00000000000..148d3053446 --- /dev/null +++ b/api/test/nostd/function_ref_test.cc @@ -0,0 +1,33 @@ +#include "opentelemetry/nostd/function_ref.h" + +#include +using namespace opentelemetry::nostd; + +int Call(function_ref f) +{ + return f(); +} + +int Return3() +{ + return 3; +} + +TEST(FunctionRefTest, Call) +{ + int x = 9; + + auto f = [&] { return x; }; + EXPECT_EQ(Call(f), 9); + + EXPECT_EQ(Call(Return3), 3); +} + +TEST(FunctionRefTest, BoolConversion) +{ + auto f = [] { return 0; }; + function_ref fref1{nullptr}; + function_ref fref2{f}; + EXPECT_TRUE(!static_cast(fref1)); + EXPECT_TRUE(static_cast(fref2)); +}