Add Inner method and support for wrapped Customizable (#8373)

Summary:
Inner method added for classes to override to return has-a relationship.  CheckedCast expanded to use Inner to return has-a relationship.

Future Customizable classes (Env, FileSystem, Statistics, etc) will use this feature

Pull Request resolved: https://github.com/facebook/rocksdb/pull/8373

Reviewed By: pdillinger

Differential Revision: D29176369

Pulled By: mrambacher

fbshipit-source-id: cfb6d7702fa365ca4e40c4a50a19e3a534e5ac43
This commit is contained in:
mrambacher 2021-06-17 08:38:30 -07:00 committed by Facebook GitHub Bot
parent ecccc63179
commit d81c2d1e3d
2 changed files with 87 additions and 5 deletions

View File

@ -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,25 +91,35 @@ 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 <typename T>
const T* CheckedCast() const {
if (IsInstanceOf(T::kClassName())) {
return static_cast<const T*>(this);
} else {
const auto inner = Inner();
if (inner != nullptr) {
return inner->CheckedCast<T>();
} else {
return nullptr;
}
}
}
template <typename T>
T* CheckedCast() {
if (IsInstanceOf(T::kClassName())) {
return static_cast<T*>(this);
} else {
auto inner = const_cast<Customizable*>(Inner());
if (inner != nullptr) {
return inner->CheckedCast<T>();
} else {
return nullptr;
}
}
}
// Checks to see if this Customizable is equivalent to other.
// This method assumes that the two objects are of the same class.
@ -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;

View File

@ -505,6 +505,70 @@ static std::unordered_map<std::string, OptionTypeInfo> inner_option_info = {
#endif // ROCKSDB_LITE
};
class InnerCustomizable : public Customizable {
public:
explicit InnerCustomizable(const std::shared_ptr<Customizable>& 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<Customizable> inner_;
};
class WrappedCustomizable1 : public InnerCustomizable {
public:
explicit WrappedCustomizable1(const std::shared_ptr<Customizable>& 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<Customizable>& w)
: InnerCustomizable(w) {}
const char* Name() const override { return kClassName(); }
static const char* kClassName() { return "Wrapped2"; }
};
TEST_F(CustomizableTest, WrappedInnerTest) {
std::shared_ptr<TestCustomizable> ac =
std::make_shared<TestCustomizable>("A");
ASSERT_TRUE(ac->IsInstanceOf("A"));
ASSERT_TRUE(ac->IsInstanceOf("TestCustomizable"));
ASSERT_EQ(ac->CheckedCast<TestCustomizable>(), ac.get());
ASSERT_EQ(ac->CheckedCast<InnerCustomizable>(), nullptr);
ASSERT_EQ(ac->CheckedCast<WrappedCustomizable1>(), nullptr);
ASSERT_EQ(ac->CheckedCast<WrappedCustomizable2>(), nullptr);
std::shared_ptr<Customizable> wc1 =
std::make_shared<WrappedCustomizable1>(ac);
ASSERT_TRUE(wc1->IsInstanceOf(WrappedCustomizable1::kClassName()));
ASSERT_EQ(wc1->CheckedCast<WrappedCustomizable1>(), wc1.get());
ASSERT_EQ(wc1->CheckedCast<WrappedCustomizable2>(), nullptr);
ASSERT_EQ(wc1->CheckedCast<InnerCustomizable>(), wc1.get());
ASSERT_EQ(wc1->CheckedCast<TestCustomizable>(), ac.get());
std::shared_ptr<Customizable> wc2 =
std::make_shared<WrappedCustomizable2>(wc1);
ASSERT_TRUE(wc2->IsInstanceOf(WrappedCustomizable2::kClassName()));
ASSERT_EQ(wc2->CheckedCast<WrappedCustomizable2>(), wc2.get());
ASSERT_EQ(wc2->CheckedCast<WrappedCustomizable1>(), wc1.get());
ASSERT_EQ(wc2->CheckedCast<InnerCustomizable>(), wc2.get());
ASSERT_EQ(wc2->CheckedCast<TestCustomizable>(), ac.get());
}
class ShallowCustomizable : public Customizable {
public:
ShallowCustomizable() {