]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
osd: migrate from boost::variant to std::variant 63414/head
authorKefu Chai <tchaikov@gmail.com>
Wed, 21 May 2025 03:38:47 +0000 (11:38 +0800)
committerKefu Chai <tchaikov@gmail.com>
Wed, 16 Jul 2025 04:49:47 +0000 (12:49 +0800)
Replace boost::variant with std::variant throughout the OSD-related
codebase. This change reduces third-party dependencies by leveraging
the C++ standard library alternative.

Changes:
- common/inline_variant.h: Replace the existing match() helper with a
  wrapper around std::visit. The previous implementation constructed a
  visitor class from given functions; the new implementation provides
  equivalent functionality using standard library primitives.

- osd/osd_types.h: Add templated operator<< overload for std::variant.
  Since boost::variant provided a built-in operator<< that we relied on,
  and std::variant does not include this functionality, we implement our
  own formatter. To avoid ambiguous overload resolution (where types
  implicitly convertible to variant alternatives could match both the
  variant formatter and their native formatters), the template requires
  at least one alternative type parameter.

This migration maintains existing functionality while eliminating the
boost::variant dependency.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
src/common/inline_variant.h
src/osd/ECTransactionL.h
src/osd/PGTransaction.h
src/osd/osd_types.cc
src/osd/osd_types.h
src/test/osd/test_pg_transaction.cc

index 28426ba7104a944d4d25e9d2b8d86fa23c792851..8d64c230ca498f6055fdefbf4d1699c0a9399870 100644 (file)
 // -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:t -*-
 // vim: ts=8 sw=4 smarttab
-/*
- * Copied from:
- * https://github.com/exclipy/inline_variant_visitor/blob/master/inline_variant.hpp
- */
 
 #ifndef INLINE_VARIANT_H
 #define INLINE_VARIANT_H
 
-#include <boost/function_types/function_arity.hpp>
-#include <boost/fusion/algorithm/transformation/transform.hpp>
-#include <boost/mpl/contains.hpp>
-#include <boost/mpl/map.hpp>
-#include <boost/mpl/vector.hpp>
-#include <boost/mpl/range_c.hpp>
-#include <boost/noncopyable.hpp>
+#include <utility>
+#include <variant>
 
-#include "function_signature.h"
-
-namespace detail {
-
-// A metafunction class for getting the argument type from a unary function or functor type
-struct function_arg_extractor
-{
-    // Function is either a function type like void(int const&), or a functor - eg. a class with void operator(int)
-    // Sets type to the argument type with the constness and referenceness stripped (eg. int)
-    template <typename Function>
-    struct apply
-    {
-    private:
-        typedef typename boost::remove_const< typename boost::remove_reference<Function>::type >::type bare_type;
-        typedef typename signature_of<bare_type>::type normalized_function_type;
-        typedef typename boost::function_types::function_arity<normalized_function_type>::type arity;
-        typedef typename boost::function_types::parameter_types<normalized_function_type>::type parameter_types;
-        typedef typename boost::function_types::result_type<normalized_function_type>::type result_type;
-
-        BOOST_STATIC_ASSERT_MSG((arity::value == 1), "make_visitor called with a non-unary function");
-
-        typedef typename boost::mpl::front<parameter_types>::type parameter_type;
-    public:
-        typedef typename boost::remove_const< typename boost::remove_reference<parameter_type>::type >::type type;
-    };
-};
-
-struct make_pair
-{
-    template <typename AType, typename Ind>
-    struct apply {
-       typedef boost::mpl::pair<AType, Ind> type;
-    };
-};
-
-// A metafunction class that asserts the second argument is in Allowed, and returns void
-template<typename Allowed>
-struct check_in
-{
-    template <typename Type1, typename Type2>
-    struct apply
-    {
-    private:
-        BOOST_STATIC_ASSERT_MSG((boost::mpl::contains<Allowed, typename boost::mpl::first<Type2>::type>::value),
-                "make_visitor called with spurious handler functions");
-    public:
-        typedef void type;
-    };
-};
-
-template <typename Seq>
-struct as_map
-{
-private:
-    struct insert_helper {
-       template <typename M, typename P>
-       struct apply
-       {
-           typedef typename boost::mpl::insert<
-               M,
-               P>::type type;
-       };
-    };
-public:
-    typedef typename boost::mpl::fold<Seq, boost::mpl::map0<>, insert_helper>::type type;
-};
-
-// A functor template suitable for passing into apply_visitor.  The constructor accepts the list of handler functions,
-// which are then exposed through a set of operator()s
-template <typename Result, typename Variant, typename... Functions>
-struct generic_visitor : boost::static_visitor<Result>, boost::noncopyable
-{
-private:
-    typedef generic_visitor<Result, Variant, Functions...> type;
-
-    // Compute the function_map type
-    typedef boost::mpl::vector<Functions...> function_types;
-    typedef typename boost::mpl::transform<function_types, function_arg_extractor>::type arg_types;
-    typedef typename boost::mpl::transform<
-        arg_types,
-       boost::mpl::range_c<int, 0, boost::mpl::size<arg_types>::value>,
-       make_pair
-       >::type pair_list;
-    typedef typename as_map<pair_list>::type fmap;
-
-    // Check that the argument types are unique
-    BOOST_STATIC_ASSERT_MSG((boost::mpl::size<fmap>::value == boost::mpl::size<arg_types>::value),
-            "make_visitor called with non-unique argument types for handler functions");
-
-    // Check that there aren't any argument types not in the variant types
-    typedef typename boost::mpl::fold<fmap, void, check_in<typename Variant::types> >::type dummy;
-
-    boost::fusion::vector<Functions...> fvec;
-
-
-    template <typename T>
-    Result apply_helper(const T& object, boost::mpl::true_) const {
-       typedef typename boost::mpl::at<fmap, T>::type Ind;
-        return boost::fusion::at<Ind>(fvec)(object);
-    }
-
-    template <typename T>
-    Result apply_helper(const T& object, boost::mpl::false_) const {
-        return Result();
-    }
-
-    BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor)
-
-public:
-    generic_visitor(BOOST_RV_REF(type) other)
-    :
-        fvec(boost::move(other.fvec))
-    {
-    }
-    generic_visitor(Functions&&... functions)
-    :
-        fvec(std::forward<Functions>(functions)...)
-    {
-    }
-
-    template <typename T>
-    Result operator()(const T& object) const {
-        typedef typename boost::mpl::has_key<fmap, T>::type correct_key;
-        BOOST_STATIC_ASSERT_MSG(correct_key::value,
-            "make_visitor called without specifying handlers for all required types");
-        return apply_helper(object, correct_key());
-    }
-};
-
-// A metafunction class for getting the return type of a function
-struct function_return_extractor
-{
-    template <typename Function>
-    struct apply : boost::function_types::result_type<typename signature_of<Function>::type>
-    {
-    };
-};
-
-// A metafunction class that asserts the two arguments are the same and returns the first one
-struct check_same
-{
-    template <typename Type1, typename Type2>
-    struct apply
-    {
-    private:
-        BOOST_STATIC_ASSERT_MSG((boost::is_same<Type1, Type2>::value),
-                "make_visitor called with functions of differing return types");
-    public:
-        typedef Type1 type;
-    };
-};
-
-// A metafunction for getting the required generic_visitor type for the set of Functions
-template <typename Variant, typename... Functions>
-struct get_generic_visitor
-{
-private:
-    typedef boost::mpl::vector<Functions...> function_types;
-    typedef typename boost::mpl::transform<
-        function_types,
-        boost::remove_const< boost::remove_reference<boost::mpl::_1> >
-    >::type bare_function_types;
-    typedef typename boost::mpl::transform<bare_function_types, function_return_extractor>::type return_types;
-
-public:
-    // Set result_type to the return type of the first function
-    typedef typename boost::mpl::front<return_types>::type result_type;
-    typedef generic_visitor<result_type, Variant, Functions...> type;
-
-private:
-    // Assert that every return type is the same as the first one
-    typedef typename boost::mpl::fold<return_types, result_type, check_same>::type dummy;
-};
-
-// Accepts a set of functions and returns an object suitable for apply_visitor
-template <typename Variant, typename... Functions>
-auto make_visitor(BOOST_RV_REF(Functions)... functions)
-    -> typename detail::get_generic_visitor<Variant, Functions...>::type
-{
-    return typename detail::get_generic_visitor<Variant, Functions...>::type(boost::forward<Functions>(functions)...);
-}
-
-}
+template <class... Functions>
+struct overloaded : Functions... { using Functions::operator()...; };
 
 template <typename Variant, typename... Functions>
-auto match(Variant const& variant, BOOST_RV_REF(Functions)... functions)
-    -> typename detail::get_generic_visitor<Variant, Functions...>::result_type
+auto match(Variant const& variant, Functions... functions)
 {
-    return boost::apply_visitor(detail::make_visitor<Variant>(
-        boost::forward<Functions>(functions)...), variant);
+    return std::visit(overloaded{std::forward<Functions>(functions)...}, variant);
 }
 
 #endif
index 16febce8cd5a4d42033127ee627231d4a506e21f..16f8cd0cf3201160dd74f1b27a3b522e92cd63a5 100644 (file)
@@ -84,7 +84,7 @@ namespace ECTransactionL {
        extent_set raw_write_set;
        for (auto &&extent: op.buffer_updates) {
          using BufferUpdate = PGTransaction::ObjectOperation::BufferUpdate;
-         if (boost::get<BufferUpdate::CloneRange>(&(extent.get_val()))) {
+         if (std::holds_alternative<BufferUpdate::CloneRange>(extent.get_val())) {
            ceph_assert(
              0 ==
              "CloneRange is not allowed, do_op should have returned ENOTSUPP");
index 5055b3b42e7f9f78bd41c23f9c34050fbc0c79ef..928d9483966ae71362d6a25822f2619243fa3828 100644 (file)
@@ -17,6 +17,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <variant>
 
 #include "common/hobject.h"
 #ifndef WITH_CRIMSON
@@ -55,7 +56,7 @@ public:
        hobject_t source; // must be temp object
       };
     };
-    using InitType = boost::variant<
+    using InitType = std::variant<
       Init::None,
       Init::Create,
       Init::Clone,
@@ -91,16 +92,16 @@ public:
       return delete_first;
     }
     bool is_delete() const {
-      return boost::get<Init::None>(&init_type) != nullptr && delete_first;
+      return std::holds_alternative<Init::None>(init_type) && delete_first;
     }
     bool is_none() const {
-      return boost::get<Init::None>(&init_type) != nullptr && !delete_first;
+      return std::holds_alternative<Init::None>(init_type) && !delete_first;
     }
     bool is_fresh_object() const {
-      return boost::get<Init::None>(&init_type) == nullptr;
+      return !std::holds_alternative<Init::None>(init_type);
     }
     bool is_rename() const {
-      return boost::get<Init::Rename>(&init_type) != nullptr;
+      return std::holds_alternative<Init::Rename>(init_type);
     }
     bool has_source(hobject_t *source = nullptr) const {
       return match(
@@ -163,7 +164,7 @@ public:
        uint64_t len;
       };
     };
-    using BufferUpdateType = boost::variant<
+    using BufferUpdateType = std::variant<
       BufferUpdate::Write,
       BufferUpdate::Zero,
       BufferUpdate::CloneRange>;
@@ -208,12 +209,11 @@ public:
        return match(
          left,
          [&](const BufferUpdate::Write &w) -> bool {
-           auto r = boost::get<BufferUpdate::Write>(&right);
+           auto r = std::get_if<BufferUpdate::Write>(&right);
            return r != nullptr && (w.fadvise_flags == r->fadvise_flags);
          },
          [&](const BufferUpdate::Zero &) -> bool {
-           auto r = boost::get<BufferUpdate::Zero>(&right);
-           return r != nullptr;
+           return std::holds_alternative<BufferUpdate::Zero>(right);
          },
          [&](const BufferUpdate::CloneRange &c) -> bool {
            return false;
@@ -225,15 +225,15 @@ public:
        return match(
          left,
          [&](const BufferUpdate::Write &w) -> BufferUpdateType {
-           auto r = boost::get<BufferUpdate::Write>(&right);
+           auto r = std::get_if<BufferUpdate::Write>(&right);
            ceph_assert(r && w.fadvise_flags == r->fadvise_flags);
            ceph::buffer::list bl = w.buffer;
            bl.append(r->buffer);
            return BufferUpdate::Write{bl, w.fadvise_flags};
          },
          [&](const BufferUpdate::Zero &z) -> BufferUpdateType {
-           auto r = boost::get<BufferUpdate::Zero>(&right);
-           ceph_assert(r);
+           auto r = std::get_if<BufferUpdate::Zero>(&right);
+           ceph_assert(r != nullptr);
            return BufferUpdate::Zero{z.len + r->len};
          },
          [&](const BufferUpdate::CloneRange &c) -> BufferUpdateType {
index 0c47e84dc6c2530118c4ac85a1afcb7999cd800c..3e04c28f31fb6b6b9a4b9d6f0245e2d1e1805ae9 100644 (file)
@@ -1416,7 +1416,7 @@ bool pool_opts_t::unset(pool_opts_t::key_t key) {
   return opts.erase(key) > 0;
 }
 
-class pool_opts_dumper_t : public boost::static_visitor<> {
+class pool_opts_dumper_t {
 public:
   pool_opts_dumper_t(const std::string& name_, Formatter* f_) :
     name(name_.c_str()), f(f_) {}
@@ -1443,7 +1443,7 @@ void pool_opts_t::dump(const std::string& name, Formatter* f) const
   if (i == opts.end()) {
       return;
   }
-  boost::apply_visitor(pool_opts_dumper_t(name, f), i->second);
+  std::visit(pool_opts_dumper_t(name, f), i->second);
 }
 
 void pool_opts_t::dump(Formatter* f) const
@@ -1455,11 +1455,11 @@ void pool_opts_t::dump(Formatter* f) const
     if (j == opts.end()) {
       continue;
     }
-    boost::apply_visitor(pool_opts_dumper_t(name, f), j->second);
+    std::visit(pool_opts_dumper_t(name, f), j->second);
   }
 }
 
-class pool_opts_encoder_t : public boost::static_visitor<> {
+class pool_opts_encoder_t {
 public:
   explicit pool_opts_encoder_t(ceph::buffer::list& bl_, uint64_t features)
     : bl(bl_),
@@ -1498,7 +1498,7 @@ void pool_opts_t::encode(ceph::buffer::list& bl, uint64_t features) const
   encode(n, bl);
   for (auto i = opts.cbegin(); i != opts.cend(); ++i) {
     encode(static_cast<int32_t>(i->first), bl);
-    boost::apply_visitor(pool_opts_encoder_t(bl, features), i->second);
+    std::visit(pool_opts_encoder_t(bl, features), i->second);
   }
   ENCODE_FINISH(bl);
 }
index 5a58dddaf80e38fafb2b58f38f3f072d9319b8fb..c6d29d1e6a659ddd96d7f242fc83e7fee130589d 100644 (file)
@@ -27,8 +27,8 @@
 #include <set>
 #include <string>
 #include <string_view>
+#include <variant>
 
-#include <boost/variant.hpp>
 #ifdef WITH_CRIMSON
 #include <boost/smart_ptr/local_shared_ptr.hpp>
 #endif
@@ -1148,7 +1148,7 @@ public:
     }
   };
 
-  typedef boost::variant<std::string,int64_t,double> value_t;
+  typedef std::variant<std::string,int64_t,double> value_t;
 
   static bool is_opt_name(const std::string& name);
   static opt_desc_t get_opt_desc(const std::string& name);
@@ -1168,7 +1168,7 @@ public:
     if (i == opts.end()) {
       return false;
     }
-    *val = boost::get<T>(i->second);
+    *val = std::get<T>(i->second);
     return true;
   }
 
@@ -1178,7 +1178,7 @@ public:
     if (i == opts.end()) {
       return std::forward<T>(default_value);
     }
-    return boost::get<T>(i->second);
+    return std::get<T>(i->second);
   }
 
   const value_t& get(key_t key) const;
@@ -1200,6 +1200,14 @@ private:
 };
 WRITE_CLASS_ENCODER_FEATURES(pool_opts_t)
 
+template <typename T, typename... Ts>
+std::ostream& operator<<(std::ostream& out, const std::variant<T, Ts...>& v) {
+  std::visit([&out](const auto& value) {
+    out << value;
+  }, v);
+  return out;
+}
+
 struct pg_merge_meta_t {
   pg_t source_pgid;
   epoch_t ready_epoch = 0;
index 6aa26920d460189fc95842b00faef259c3420175..dfd7d79c97897e12fb648480ed1a0333cbbd6ac6 100644 (file)
@@ -29,7 +29,7 @@ TEST(pgtransaction, simple)
     [&](const pair<const hobject_t, PGTransaction::ObjectOperation> &p) {
       ASSERT_EQ(p.first, h);
       using T = PGTransaction::ObjectOperation::Init;
-      ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+      ASSERT_TRUE(std::holds_alternative<T::None>(p.second.init_type));
       ++num;
     });
   ASSERT_EQ(num, 1u);
@@ -50,13 +50,13 @@ TEST(pgtransaction, clone_safe_create_traverse)
       using T = PGTransaction::ObjectOperation::Init;
       if (num == 0) {
        ASSERT_EQ(p.first, h);
-       ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::Clone>(p.second.init_type));
        ASSERT_EQ(
-         boost::get<T::Clone>(&p.second.init_type)->source,
+         std::get_if<T::Clone>(&p.second.init_type)->source,
          h2);
       } else if (num == 1) {
        ASSERT_EQ(p.first, h2);
-       ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::None>(p.second.init_type));
       } else {
        ASSERT_LT(num, 2u);
       }
@@ -83,19 +83,19 @@ TEST(pgtransaction, clone_safe_create_traverse2)
       using T = PGTransaction::ObjectOperation::Init;
       if (num == 0) {
        ASSERT_EQ(p.first, h);
-       ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::Clone>(p.second.init_type));
        ASSERT_EQ(
-         boost::get<T::Clone>(&p.second.init_type)->source,
+         std::get_if<T::Clone>(&p.second.init_type)->source,
          h2);
       } else if (num == 1) {
        ASSERT_EQ(p.first, h2);
-       ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::Clone>(p.second.init_type));
        ASSERT_EQ(
-         boost::get<T::Clone>(&p.second.init_type)->source,
+         std::get_if<T::Clone>(&p.second.init_type)->source,
          h3);
       } else if (num == 2) {
        ASSERT_EQ(p.first, h3);
-       ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::None>(p.second.init_type));
       } else {
        ASSERT_LT(num, 3u);
       }
@@ -120,9 +120,9 @@ TEST(pgtransaction, clone_safe_create_traverse3)
       if (p.first == h) {
        ASSERT_TRUE(p.second.is_delete());
       } else if (p.first == h2) {
-       ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+       ASSERT_TRUE(std::holds_alternative<T::Clone>(p.second.init_type));
        ASSERT_EQ(
-         boost::get<T::Clone>(&p.second.init_type)->source,
+         std::get_if<T::Clone>(&p.second.init_type)->source,
          h3);
       }
       ASSERT_LT(num, 2u);