diff --git a/include/rocksdb/customizable.h b/include/rocksdb/customizable.h index 366c7563f..24ddfa56c 100644 --- a/include/rocksdb/customizable.h +++ b/include/rocksdb/customizable.h @@ -81,6 +81,9 @@ class Customizable : public Configurable { // potential names (e.g. "PosixEnv", "DefaultEnv") may also wish to override // this method. // + // Note that IsInstanceOf only uses the "is-a" relationship and not "has-a". + // Wrapped classes that have an Inner "has-a" should not be returned. + // // @param name The name of the instance to find. // Returns true if the class is an instance of the input name. virtual bool IsInstanceOf(const std::string& name) const { @@ -88,14 +91,19 @@ class Customizable : public Configurable { } // Returns the named instance of the Customizable as a T*, or nullptr if not - // found. This method uses IsInstanceOf to find the appropriate class instance - // and then casts it to the expected return type. + // found. This method uses IsInstanceOf/Inner to find the appropriate class + // instance and then casts it to the expected return type. template const T* CheckedCast() const { if (IsInstanceOf(T::kClassName())) { return static_cast(this); } else { - return nullptr; + const auto inner = Inner(); + if (inner != nullptr) { + return inner->CheckedCast(); + } else { + return nullptr; + } } } @@ -104,7 +112,12 @@ class Customizable : public Configurable { if (IsInstanceOf(T::kClassName())) { return static_cast(this); } else { - return nullptr; + auto inner = const_cast(Inner()); + if (inner != nullptr) { + return inner->CheckedCast(); + } else { + return nullptr; + } } } @@ -124,8 +137,13 @@ class Customizable : public Configurable { // @see Configurable::GetOption for more details Status GetOption(const ConfigOptions& config_options, const std::string& name, std::string* value) const override; - #endif // ROCKSDB_LITE + + // Returns the inner class when a Customizable implements a has-a (wrapped) + // relationship. Derived classes that implement a has-a must override this + // method in order to get CheckedCast to function properly. + virtual const Customizable* Inner() const { return nullptr; } + protected: // Given a name (e.g. rocksdb.my.type.opt), returns the short name (opt) std::string GetOptionName(const std::string& long_name) const override; diff --git a/options/customizable_test.cc b/options/customizable_test.cc index 3263b6ac9..4dc648e80 100644 --- a/options/customizable_test.cc +++ b/options/customizable_test.cc @@ -505,6 +505,70 @@ static std::unordered_map inner_option_info = { #endif // ROCKSDB_LITE }; +class InnerCustomizable : public Customizable { + public: + explicit InnerCustomizable(const std::shared_ptr& w) + : inner_(w) {} + static const char* kClassName() { return "Inner"; } + bool IsInstanceOf(const std::string& name) const override { + if (name == kClassName()) { + return true; + } else { + return Customizable::IsInstanceOf(name); + } + } + + protected: + const Customizable* Inner() const override { return inner_.get(); } + + private: + std::shared_ptr inner_; +}; + +class WrappedCustomizable1 : public InnerCustomizable { + public: + explicit WrappedCustomizable1(const std::shared_ptr& w) + : InnerCustomizable(w) {} + const char* Name() const override { return kClassName(); } + static const char* kClassName() { return "Wrapped1"; } +}; + +class WrappedCustomizable2 : public InnerCustomizable { + public: + explicit WrappedCustomizable2(const std::shared_ptr& w) + : InnerCustomizable(w) {} + const char* Name() const override { return kClassName(); } + static const char* kClassName() { return "Wrapped2"; } +}; + +TEST_F(CustomizableTest, WrappedInnerTest) { + std::shared_ptr ac = + std::make_shared("A"); + + ASSERT_TRUE(ac->IsInstanceOf("A")); + ASSERT_TRUE(ac->IsInstanceOf("TestCustomizable")); + ASSERT_EQ(ac->CheckedCast(), ac.get()); + ASSERT_EQ(ac->CheckedCast(), nullptr); + ASSERT_EQ(ac->CheckedCast(), nullptr); + ASSERT_EQ(ac->CheckedCast(), nullptr); + std::shared_ptr wc1 = + std::make_shared(ac); + + ASSERT_TRUE(wc1->IsInstanceOf(WrappedCustomizable1::kClassName())); + ASSERT_EQ(wc1->CheckedCast(), wc1.get()); + ASSERT_EQ(wc1->CheckedCast(), nullptr); + ASSERT_EQ(wc1->CheckedCast(), wc1.get()); + ASSERT_EQ(wc1->CheckedCast(), ac.get()); + + std::shared_ptr wc2 = + std::make_shared(wc1); + ASSERT_TRUE(wc2->IsInstanceOf(WrappedCustomizable2::kClassName())); + ASSERT_EQ(wc2->CheckedCast(), wc2.get()); + ASSERT_EQ(wc2->CheckedCast(), wc1.get()); + ASSERT_EQ(wc2->CheckedCast(), wc2.get()); + ASSERT_EQ(wc2->CheckedCast(), ac.get()); +} + class ShallowCustomizable : public Customizable { public: ShallowCustomizable() {