Skip to content

Commit

Permalink
Add function_ref (open-telemetry#61)
Browse files Browse the repository at this point in the history
* Add function_ref

* Add function_ref
  • Loading branch information
rnburn committed Apr 24, 2020
1 parent 9e2418b commit 7479aa1
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 2 deletions.
83 changes: 83 additions & 0 deletions api/include/opentelemetry/nostd/function_ref.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#pragma once

#include <memory>
#include <type_traits>

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace nostd
{
template <class Sig>
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 R, class... Args>
class function_ref<R(Args...)>
{
void *callable_ = nullptr;
R (*invoker_)(void *, Args...) = nullptr;

template <class F>
using FunctionPointer = decltype(std::addressof(std::declval<F &>()));

template <class F>
void BindTo(F &f) noexcept
{
callable_ = static_cast<void *>(std::addressof(f));
invoker_ = [](void *callable_, Args... args) -> R {
return (*static_cast<FunctionPointer<F>>(callable_))(std::forward<Args>(args)...);
};
}

template <class R_in, class... Args_in>
void BindTo(R_in (*f)(Args_in...)) noexcept
{
using F = decltype(f);
if (f == nullptr)
{
return BindTo(nullptr);
}
callable_ = reinterpret_cast<void *>(f);
invoker_ = [](void *callable_, Args... args) -> R {
return (F(callable_))(std::forward<Args>(args)...);
};
}

void BindTo(std::nullptr_t) noexcept
{
callable_ = nullptr;
invoker_ = nullptr;
}

public:
template <
class F,
typename std::enable_if<!std::is_same<function_ref, typename std::decay<F>::type>::value,
int>::type = 0,
typename std::enable_if<
std::is_convertible<typename std::result_of<F &(Args...)>::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>(args)...); }

explicit operator bool() const { return invoker_; }
};
} // namespace nostd
OPENTELEMETRY_END_NAMESPACE
11 changes: 11 additions & 0 deletions api/test/nostd/BUILD
Original file line number Diff line number Diff line change
@@ -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 = [
Expand Down
4 changes: 2 additions & 2 deletions api/test/nostd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
33 changes: 33 additions & 0 deletions api/test/nostd/function_ref_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "opentelemetry/nostd/function_ref.h"

#include <gtest/gtest.h>
using namespace opentelemetry::nostd;

int Call(function_ref<int()> 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<int()> fref1{nullptr};
function_ref<int()> fref2{f};
EXPECT_TRUE(!static_cast<bool>(fref1));
EXPECT_TRUE(static_cast<bool>(fref2));
}

0 comments on commit 7479aa1

Please sign in to comment.