#pragma once #include #include #include #include namespace c10 { // Represents a name of the form "foo.bar.baz" struct QualifiedName { QualifiedName() {} // `name` can be a dotted string, like "foo.bar.baz", or just a bare name. /* implicit */ QualifiedName(const std::string& name) { TORCH_CHECK(!name.empty()); // split the string into its atoms. size_t startSearchFrom = 0; size_t pos = name.find(delimiter_, startSearchFrom); while (pos != std::string::npos) { auto atom = name.substr(startSearchFrom, pos - startSearchFrom); AT_ASSERTM( atom.size() > 0, "Invalid name for qualified name: '", name, "'"); atoms_.push_back(std::move(atom)); startSearchFrom = pos + 1; pos = name.find(delimiter_, startSearchFrom); } auto finalAtom = name.substr(startSearchFrom, pos - startSearchFrom); AT_ASSERTM( finalAtom.size() > 0, "Invalid name for qualified name: '", name, "'"); atoms_.push_back(std::move(finalAtom)); cacheAccessors(); } explicit QualifiedName(std::vector atoms) { for (const auto& atom : atoms) { TORCH_CHECK(!atom.empty(), "Atom cannot be empty"); TORCH_CHECK( atom.find(delimiter_) == std::string::npos, "Delimiter not allowed in atom"); } atoms_ = atoms; cacheAccessors(); } // Unnecessary copy. Ideally we'd use somoething like std::string_view. /* implicit */ QualifiedName(const char* name) : QualifiedName(std::string(name)) {} // `name` must be a bare name (no dots!) explicit QualifiedName(const QualifiedName& prefix, std::string name) { TORCH_INTERNAL_ASSERT(!name.empty()); TORCH_INTERNAL_ASSERT(name.find(delimiter_) == std::string::npos); atoms_.insert(atoms_.begin(), prefix.atoms_.begin(), prefix.atoms_.end()); atoms_.push_back(std::move(name)); cacheAccessors(); } // Is `this` a prefix of `other`? // For example, "foo.bar" is a prefix of "foo.bar.baz" bool isPrefixOf(const QualifiedName& other) const { const auto& thisAtoms = atoms_; const auto& otherAtoms = other.atoms_; if (thisAtoms.size() > otherAtoms.size()) { // Can't be a prefix if it's bigger return false; } for (size_t i = 0; i < thisAtoms.size(); i++) { if (thisAtoms[i] != otherAtoms[i]) { return false; } } return true; } // The fully qualified name, like "foo.bar.baz" const std::string& qualifiedName() const { return qualifiedName_; } // The leading qualifier, like "foo.bar" const std::string& prefix() const { return prefix_; } // The base name, like "baz" const std::string& name() const { return name_; } const std::vector& atoms() const { return atoms_; } bool operator==(const QualifiedName& other) const { return this->qualifiedName_ == other.qualifiedName_; } bool operator!=(const QualifiedName& other) const { return !(*this == other); } private: char delimiter_ = '.'; void cacheAccessors() { qualifiedName_ = Join(std::string(1, delimiter_), atoms_); if (atoms_.size() > 1) { ArrayRef view(atoms_); const auto prefixView = view.slice(0, view.size() - 1); prefix_ = Join(".", prefixView); } if (atoms_.size() >= 1) { name_ = atoms_.back(); } } // The actual list of names, like "{foo, bar, baz}" std::vector atoms_; /* * Cached accessors, derived from `atoms_`. */ std::string qualifiedName_; std::string prefix_; std::string name_; }; } // namespace c10 namespace std { template <> struct hash { size_t operator()(const c10::QualifiedName& n) const noexcept { return std::hash()(n.qualifiedName()); } }; } // namespace std