From a0f36e128a022bd764639032d03792917ba7dd1d Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Tue, 25 Jul 2017 16:10:51 -0400 Subject: [PATCH] common: add std::not_fn to backport14.h and unit tests Signed-off-by: Casey Bodley --- src/common/backport14.h | 44 ++++++++++++++ src/test/common/CMakeLists.txt | 6 +- src/test/common/test_backport14.cc | 94 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/test/common/test_backport14.cc diff --git a/src/common/backport14.h b/src/common/backport14.h index d56e24c3e51..3e6505d468d 100644 --- a/src/common/backport14.h +++ b/src/common/backport14.h @@ -27,6 +27,8 @@ template using remove_reference_t = typename std::remove_reference::type; template using result_of_t = typename std::result_of::type; +template +using decay_t = typename std::decay::type; namespace _backport14 { template @@ -75,10 +77,52 @@ template constexpr std::size_t size(const T (&array)[N]) noexcept { return N; } + +/// http://en.cppreference.com/w/cpp/utility/functional/not_fn +// this implementation uses c++14's result_of_t (above) instead of the c++17 +// invoke_result_t, and so may not behave correctly when SFINAE is required +template +class not_fn_result { + using DecayF = decay_t; + DecayF fn; + public: + explicit not_fn_result(F&& f) : fn(std::forward(f)) {} + not_fn_result(not_fn_result&& f) = default; + not_fn_result(const not_fn_result& f) = default; + + template + auto operator()(Args&&... args) & + -> decltype(!std::declval>()) { + return !fn(std::forward(args)...); + } + template + auto operator()(Args&&... args) const& + -> decltype(!std::declval>()) { + return !fn(std::forward(args)...); + } + + template + auto operator()(Args&&... args) && + -> decltype(!std::declval>()) { + return !std::move(fn)(std::forward(args)...); + } + template + auto operator()(Args&&... args) const&& + -> decltype(!std::declval>()) { + return !std::move(fn)(std::forward(args)...); + } +}; + +template +not_fn_result not_fn(F&& fn) { + return not_fn_result(std::forward(fn)); +} + } // namespace _backport17 using _backport14::make_unique; using _backport17::size; using _backport14::max; +using _backport17::not_fn; } // namespace ceph #endif // CEPH_COMMON_BACKPORT14_H diff --git a/src/test/common/CMakeLists.txt b/src/test/common/CMakeLists.txt index 17034b67aee..9a2be3df293 100644 --- a/src/test/common/CMakeLists.txt +++ b/src/test/common/CMakeLists.txt @@ -271,4 +271,8 @@ add_executable(unittest_iso_8601 test_iso_8601.cc) target_link_libraries(unittest_iso_8601 ceph-common) add_ceph_unittest(unittest_iso_8601 - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_hostname) + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_iso_8601) + +add_executable(unittest_backport14 test_backport14.cc) +add_ceph_unittest(unittest_backport14 + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_backport14) diff --git a/src/test/common/test_backport14.cc b/src/test/common/test_backport14.cc new file mode 100644 index 00000000000..63ef5d0e7b3 --- /dev/null +++ b/src/test/common/test_backport14.cc @@ -0,0 +1,94 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Casey Bodley + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "common/backport14.h" // include first: tests that header is standalone +#include + +int int_func() { return 1; } +bool bool_func0() { return true; } +bool bool_func1(int a) { return true; } +bool bool_func2(const std::string& a, int b) { return true; } + +// given a callable and argument list, test that the result of ceph::not_fn +// evaluates to false as both an lvalue and rvalue +template +void test_not(F&& fn, Args&&... args) +{ + auto res = ceph::not_fn(std::forward(fn)); + // test res as lvalue + EXPECT_FALSE(res(std::forward(args)...)); + // test res as rvalue + // note: this forwards args twice, but it's okay if none are rvalues + EXPECT_FALSE(std::move(res)(std::forward(args)...)); +} + +TEST(Backport14, not_fn) +{ + // function pointers + test_not(int_func); + test_not(&int_func); + test_not(bool_func0); + test_not(&bool_func0); + test_not(bool_func1, 5); + test_not(bool_func2, "foo", 5); + + // lambdas + auto int_lambda = [] { return 1; }; + auto bool_lambda0 = [] { return true; }; + auto bool_lambda1 = [] (int a) { return true; }; + auto bool_lambda2 = [] (const std::string& a, int b) { return true; }; + + test_not(int_lambda); + test_not(bool_lambda0); + test_not(bool_lambda1, 5); + test_not(bool_lambda2, "foo", 5); + + // functors + struct int_functor { + int operator()() { return 1; } + }; + test_not(int_functor{}); + + struct bool_functor { + bool operator()() { return true; } + bool operator()(int a) { return true; } + bool operator()(const std::string& a, int b) { return true; } + }; + + test_not(bool_functor{}); + test_not(bool_functor{}, 5); + test_not(bool_functor{}, "foo", 5); + + // lvalue-only overload + struct lvalue_only_functor { + bool operator()() & { return true; } // no overload for rvalue + }; + auto lvalue_result = ceph::not_fn(lvalue_only_functor{}); + EXPECT_FALSE(lvalue_result()); + // should not compile: + // EXPECT_FALSE(std::move(lvalue_result)()); + + // rvalue-only overload + struct rvalue_only_functor { + bool operator()() && { return true; } // no overload for lvalue + }; + EXPECT_FALSE(ceph::not_fn(rvalue_only_functor{})()); + auto lvalue_functor = rvalue_only_functor{}; + EXPECT_FALSE(ceph::not_fn(lvalue_functor)()); // lvalue functor, rvalue result + // should not compile: + // auto lvalue_result2 = ceph::not_fn(rvalue_only_functor{}); + // EXPECT_FALSE(lvalue_result2()); +} -- 2.39.5