From ee0d7e9e1bef3a1b06c5e57cfae971bdbe689059 Mon Sep 17 00:00:00 2001 From: "Adam C. Emerson" Date: Fri, 27 Oct 2017 22:46:16 -0400 Subject: [PATCH] common: Add functions to deal with boost::optional Simply provide what's obvious but missing. Signed-off-by: Adam C. Emerson --- src/common/convenience.h | 38 +++++++++++++++++++++++ src/test/common/CMakeLists.txt | 4 +++ src/test/common/test_convenience.cc | 48 +++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/test/common/test_convenience.cc diff --git a/src/common/convenience.h b/src/common/convenience.h index b831cbd7260..1c935abc526 100644 --- a/src/common/convenience.h +++ b/src/common/convenience.h @@ -17,6 +17,7 @@ #include #include +#include #include #include "common/backport14.h" @@ -161,4 +162,41 @@ inline auto with_shared_lock(Mutex&& mutex, Fun&& fun, Args&&... args) #define SHUNIQUE_LOCK_T(m) \ ::ceph::shunique_lock> +// boost::optional is wonderful! Unfortunately it lacks a function for +// the thing you would most obviously want to do with it: apply a +// function to its contents. + +// There are two obvious candidates. The first is a function that +// takes a function and an optional value and returns an optional +// value, either holding the return value of the function or holding +// nothing. +// +// I'd considered making more overloads for mutable lvalue +// references, but those are going a bit beyond likely use cases. +// +template +auto maybe_do(const boost::optional& t, F&& f) -> + boost::optional)>> +{ + if (t) + return { std::forward(f)(*t) }; + else + return boost::none; +} + +// The other obvious function takes an optional but returns an +// ‘unwrapped’ value, either the result of evaluating the function or +// a provided alternate value. +// +template +auto maybe_do_or(const boost::optional& t, F&& f, U&& u) -> + ceph::result_of_t)> +{ + static_assert(std::is_convertible>::value, + "Alternate value must be convertible to function return type."); + if (t) + return std::forward(f)(*t); + else + return std::forward(u); +} #endif // CEPH_COMMON_CONVENIENCE_H diff --git a/src/test/common/CMakeLists.txt b/src/test/common/CMakeLists.txt index 0fffb43b11d..4b123446266 100644 --- a/src/test/common/CMakeLists.txt +++ b/src/test/common/CMakeLists.txt @@ -267,3 +267,7 @@ add_ceph_unittest(unittest_iso_8601) add_executable(unittest_backport14 test_backport14.cc) add_ceph_unittest(unittest_backport14) + +add_executable(unittest_convenience test_convenience.cc) +target_link_libraries(unittest_convenience ceph-common) +add_ceph_unittest(unittest_convenience) diff --git a/src/test/common/test_convenience.cc b/src/test/common/test_convenience.cc new file mode 100644 index 00000000000..4029eefde99 --- /dev/null +++ b/src/test/common/test_convenience.cc @@ -0,0 +1,48 @@ +// -*- 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/convenience.h" // include first: tests that header is standalone + +#include +#include +#include + +// A just god would not allow the C++ standard to make taking the +// address of member functions in the standard library undefined behavior. +static std::string::size_type l(const std::string& s) { + return s.size(); +} + +TEST(Convenience, MaybeDo) +{ + boost::optional s("qwerty"); + boost::optional t; + auto r = maybe_do(s, l); + EXPECT_TRUE(r); + EXPECT_EQ(*r, s->size()); + + EXPECT_FALSE(maybe_do(t, l)); +} + +TEST(Convenience, MaybeDoOr) +{ + const boost::optional s("qwerty"); + const boost::optional t; + auto r = maybe_do_or(s, l, 0); + EXPECT_EQ(r, s->size()); + + EXPECT_EQ(maybe_do_or(t, l, 0), 0); +} -- 2.39.5