Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-03-26 01:51:12

0001 #ifndef FWCore_ParameterSet_ParameterDescriptionNode_h
0002 #define FWCore_ParameterSet_ParameterDescriptionNode_h
0003 
0004 // This is a base class for the class that describes
0005 // the parameters that are allowed or required to be
0006 // in a ParameterSet.  It is also a base class for
0007 // other more complex logical structures which describe
0008 // which combinations of parameters are allowed to be
0009 // in a ParameterSet.
0010 
0011 #include "FWCore/Utilities/interface/value_ptr.h"
0012 
0013 #include <string>
0014 #include <set>
0015 #include <iosfwd>
0016 #include <memory>
0017 #include <variant>
0018 #include <optional>
0019 #include <unordered_map>
0020 #include <vector>
0021 #include <cassert>
0022 
0023 namespace edm {
0024 
0025   class ParameterSet;
0026   template <typename T>
0027   class ParameterDescriptionCases;
0028   class DocFormatHelper;
0029 
0030   // Originally these were defined such that the values were the
0031   // same as in the ParameterSet Entry class and the validation
0032   // depended on that.  But at the moment I'm typing this comment,
0033   // the code no longer depends on the values being the same (which
0034   // is probably good because nothing enforces the correspondence,
0035   // a task for the future when someone has free time would be
0036   // to define the values in a common header, but that would involve
0037   // significant changes to ParameterSet ...)
0038   enum ParameterTypes {
0039     k_int32 = 'I',
0040     k_vint32 = 'i',
0041     k_uint32 = 'U',
0042     k_vuint32 = 'u',
0043     k_int64 = 'L',
0044     k_vint64 = 'l',
0045     k_uint64 = 'X',
0046     k_vuint64 = 'x',
0047     k_double = 'D',
0048     k_vdouble = 'd',
0049     k_bool = 'B',
0050     k_stringRaw = 'Z',
0051     k_vstringRaw = 'z',
0052     k_stringHex = 'S',
0053     k_vstringHex = 's',
0054     k_EventID = 'E',
0055     k_VEventID = 'e',
0056     k_LuminosityBlockID = 'M',
0057     k_VLuminosityBlockID = 'm',
0058     k_InputTag = 't',
0059     k_VInputTag = 'v',
0060     k_ESInputTag = 'g',
0061     k_VESInputTag = 'G',
0062     k_FileInPath = 'F',
0063     k_LuminosityBlockRange = 'A',
0064     k_VLuminosityBlockRange = 'a',
0065     k_EventRange = 'R',
0066     k_VEventRange = 'r',
0067     k_PSet = 'Q',
0068     k_VPSet = 'q'
0069   };
0070 
0071   std::string parameterTypeEnumToString(ParameterTypes iType);
0072 
0073   enum class ParameterModifier : unsigned char { kNone, kOptional, kObsolete };
0074   inline ParameterModifier modifierIsOptional(bool iOptional) {
0075     return iOptional ? ParameterModifier::kOptional : ParameterModifier::kNone;
0076   }
0077 
0078   namespace cfi {
0079     struct Paths {
0080       //This is the 'path' through the cms.PSet hierarchy.
0081       // E.g. foo.bar.tar would have a Path for foo, bar, and tar with
0082       // tar having a null nodes_ variable.
0083       std::optional<std::unordered_map<std::string, Paths>> nodes_;
0084     };
0085     struct Typed {};
0086     struct ClassFile {
0087       void parameterMustBeTyped() {
0088         Paths* p = &fullPaths_;
0089         for (auto n : presentPath_) {
0090           if (not p->nodes_) {
0091             p->nodes_ = std::unordered_map<std::string, Paths>();
0092           }
0093           p = &(p->nodes_.value().emplace(n, Paths{})).first->second;
0094         }
0095       }
0096       void pushNode(std::string_view iNode) { presentPath_.push_back(iNode); }
0097       void popNode() {
0098         assert(not presentPath_.empty());
0099         presentPath_.pop_back();
0100       }
0101 
0102       Paths releasePaths() { return std::move(fullPaths_); }
0103 
0104     private:
0105       std::vector<std::string_view> presentPath_;
0106       Paths fullPaths_;
0107     };
0108     struct Untyped {
0109       Untyped(Paths iPaths) : paths_(iPaths) {}
0110       bool needToSwitchToTyped(std::string_view iNode) {
0111         presentPath_.push_back(iNode);
0112         if (not paths_.nodes_.has_value()) {
0113           return false;
0114         }
0115         const Paths* p = &paths_;
0116         for (auto const& n : presentPath_) {
0117           if (not paths_.nodes_.has_value()) {
0118             return false;
0119           }
0120           auto f = paths_.nodes_->find(std::string(n));
0121           if (f == paths_.nodes_->end()) {
0122             return false;
0123           }
0124           p = &f->second;
0125         }
0126         return not p->nodes_.has_value();
0127       }
0128       void popNode() {
0129         assert(not presentPath_.empty());
0130         presentPath_.pop_back();
0131       }
0132 
0133     private:
0134       std::vector<std::string_view> presentPath_;
0135       Paths paths_;
0136     };
0137 
0138     using CfiOptions = std::variant<cfi::Typed, cfi::ClassFile, cfi::Untyped>;
0139 
0140     inline void parameterMustBeTyped(CfiOptions& iOps) noexcept {
0141       if (std::holds_alternative<cfi::ClassFile>(iOps)) {
0142         std::get<cfi::ClassFile>(iOps).parameterMustBeTyped();
0143       }
0144     }
0145     inline void parameterMustBeTyped(CfiOptions& iOps, std::string_view iNode) noexcept {
0146       if (std::holds_alternative<cfi::ClassFile>(iOps)) {
0147         auto& d = std::get<cfi::ClassFile>(iOps);
0148         d.pushNode(iNode);
0149         d.parameterMustBeTyped();
0150         d.popNode();
0151       }
0152     }
0153     [[nodiscard]] inline bool shouldWriteUntyped(CfiOptions const& iOps) noexcept {
0154       return std::holds_alternative<cfi::Untyped>(iOps);
0155     }
0156 
0157     struct NodeGuard {
0158       NodeGuard(CfiOptions& iOp) : options_(&iOp) {}
0159       NodeGuard() = delete;
0160       NodeGuard(NodeGuard const&) = delete;
0161       NodeGuard& operator=(NodeGuard const&) = delete;
0162       NodeGuard(NodeGuard&& iOther) : options_{iOther.options_} { iOther.options_ = nullptr; }
0163       NodeGuard& operator=(NodeGuard&& iOther) {
0164         NodeGuard temp{std::move(iOther)};
0165         options_ = temp.options_;
0166         temp.options_ = nullptr;
0167         return *this;
0168       }
0169       ~NodeGuard() {
0170         if (nullptr == options_) {
0171           return;
0172         }
0173         if (std::holds_alternative<ClassFile>(*options_)) {
0174           std::get<ClassFile>(*options_).popNode();
0175         } else if (std::holds_alternative<Untyped>(*options_)) {
0176           std::get<Untyped>(*options_).popNode();
0177         }
0178       }
0179       CfiOptions* options_;
0180     };
0181 
0182     [[nodiscard]] inline std::pair<bool, NodeGuard> needToSwitchToTyped(std::string_view iNode,
0183                                                                         CfiOptions& iOpt) noexcept {
0184       if (std::holds_alternative<Untyped>(iOpt)) {
0185         return std::pair(std::get<Untyped>(iOpt).needToSwitchToTyped(iNode), NodeGuard(iOpt));
0186       } else if (std::holds_alternative<ClassFile>(iOpt)) {
0187         std::get<ClassFile>(iOpt).pushNode(iNode);
0188       }
0189       return std::pair(false, NodeGuard(iOpt));
0190     }
0191   }  // namespace cfi
0192   using CfiOptions = cfi::CfiOptions;
0193 
0194   struct ParameterTypeToEnum {
0195     template <class T>
0196     static ParameterTypes toEnum();
0197   };
0198 
0199   class Comment {
0200   public:
0201     Comment();
0202     explicit Comment(std::string const& iComment);
0203     explicit Comment(char const* iComment);
0204     std::string const& comment() const { return comment_; }
0205 
0206   private:
0207     std::string comment_;
0208   };
0209 
0210   class ParameterDescriptionNode {
0211   public:
0212     using Modifier = ParameterModifier;
0213 
0214     ParameterDescriptionNode() {}
0215 
0216     explicit ParameterDescriptionNode(Comment const& iComment) : comment_(iComment.comment()) {}
0217 
0218     virtual ~ParameterDescriptionNode();
0219 
0220     virtual ParameterDescriptionNode* clone() const = 0;
0221 
0222     std::string const& comment() const { return comment_; }
0223     void setComment(std::string const& value);
0224     void setComment(char const* value);
0225 
0226     // The validate function should do one of three things, find that the
0227     // node "exists", make the node "exist" by inserting missing parameters
0228     // or throw.  The only exception to this rule occurs when the argument
0229     // named "modifier" is kOptional or kObsolete, which should only be possible for the
0230     // top level nodes of a ParameterSetDescription.  When a parameter is
0231     // found or inserted its label is added into the list of validatedLabels.
0232     void validate(ParameterSet& pset, std::set<std::string>& validatedLabels, Modifier modifier) const {
0233       validate_(pset, validatedLabels, modifier);
0234     }
0235 
0236     // As long as it has default values, this will attempt to write
0237     // parameters associated with a node into a cfi file that is
0238     // being automatically generated.  It is quite possible for
0239     // to produce a cfi that will fail validation.  In some cases,
0240     // this will imply the user is required to supply certain missing
0241     // parameters that do not appear in the cfi and do not have defaults
0242     // in the description.  It is also possible to create a pathological
0243     // ParameterSetDescription where the algorithm fails to write
0244     // a valid cfi, in some cases the description can be so pathological
0245     // that it is impossible to write a cfi that will pass validation.
0246     void writeCfi(std::ostream& os,
0247                   Modifier modifier,
0248                   bool& startWithComma,
0249                   int indentation,
0250                   CfiOptions& options,
0251                   bool& wroteSomething) const {
0252       writeCfi_(os, modifier, startWithComma, indentation, options, wroteSomething);
0253     }
0254 
0255     // Print out the description in human readable format
0256     void print(std::ostream& os, Modifier modifier, bool writeToCfi, DocFormatHelper& dfh) const;
0257 
0258     bool hasNestedContent() const { return hasNestedContent_(); }
0259 
0260     void printNestedContent(std::ostream& os, bool optional, DocFormatHelper& dfh) const;
0261 
0262     // The next three functions are only called by the logical nodes
0263     // on their subnodes.  When executing these functions, the
0264     // insertion of missing parameters does not occur.
0265 
0266     // Usually checks to see if a parameter exists in the configuration, but
0267     // if the node is a logical node, then it returns the value of the logical
0268     // expression.
0269     bool exists(ParameterSet const& pset) const { return exists_(pset); }
0270 
0271     // For most nodes, this simply returns the same value as the exists
0272     // function.  But for AND nodes this returns true if either its subnodes
0273     // exists.  Used by operator&& during validation, if either of an AND node's
0274     // subnodes exists, then both subnodes get validated.
0275     bool partiallyExists(ParameterSet const& pset) const { return partiallyExists_(pset); }
0276 
0277     // For most nodes, this simply returns the same value as the exists
0278     // function. It is different for an XOR node.  It counts
0279     // XOR subnodes whose exists function returns true.  And it
0280     // does this recursively into XOR nodes that are contained in
0281     // other XOR nodes.
0282     // Used by operator^ during validation:
0283     // -- if it returns more than 1, then validation will throw,
0284     // -- if it returns exactly one, then only the nonzero subnode gets validated
0285     // -- if it returns zero, then validation tries to validate the first node and
0286     // then rechecks to see what the missing parameter insertion did (there could
0287     // be side effects on the nodes that were not validated)
0288     int howManyXORSubNodesExist(ParameterSet const& pset) const { return howManyXORSubNodesExist_(pset); }
0289 
0290     /* Validation puts requirements on which parameters can and cannot exist
0291     within a ParameterSet.  The evaluation of whether a ParameterSet passes
0292     or fails the rules in the ParameterSetDescription is complicated by
0293     the fact that we allow missing parameters to be injected into the
0294     ParameterSet during validation.  One must worry whether injecting a
0295     missing parameter invalidates some other part of the ParameterSet that
0296     was already checked and determined to be OK.  The following restrictions
0297     avoid that problem.
0298 
0299         - The same parameter labels cannot occur in different nodes of the
0300         same ParameterSetDescription.  There are two exceptions to this.
0301         Nodes that are contained in the cases of a ParameterSwitch or the
0302         subnodes of an "exclusive or" are allowed to use the same labels.
0303 
0304         - If insertion is necessary to make an "exclusive or" node pass
0305         validation, then the insertion could make more than one of the
0306         possibilities evaluate true.  This must be checked for after the
0307         insertions occur. The behavior is to throw a Configuration exception
0308         if this problem is encountered. (Example: (A && B) ^ (A && C) where
0309         C already exists in the ParameterSet but A and B do not.  A and B
0310         get inserted by the algorithm, because it tries to make the first
0311         possibility true when all fail without insertion.  Then both
0312         parts of the "exclusive or" pass, which is a validation failure).
0313 
0314         - Another potential problem is that a parameter insertion related
0315         to one ParameterDescription could match unrelated wildcards causing
0316         other validation requirements to change from being passing to failing
0317         or vice versa.  This makes it almost impossible to determine if a
0318         ParameterSet passes validation.  Each time you try to loop through
0319         and check, the result of validation could change.  To avoid this problem,
0320         a list is maintained of the type for all wildcards.  Another list is
0321         maintained for the type of all parameters.  As new items are added
0322         we check for collisions.  The function that builds the ParameterSetDescription,
0323         will throw if this rule is violated.  At the moment, the criteria
0324         for a collision is matching types between a parameter and a wildcard.
0325         (This criteria is overrestrictive.  With some additional CPU and
0326         code development the restriction could be loosened to parameters that
0327         might be injected cannot match the type, trackiness, and wildcard label
0328         pattern of any wildcard that requires a match.  And further this
0329         could not apply to wildcards on different branches of a ParameterSwitch
0330         or "exclusive or".)
0331 
0332     These restrictions have the additional benefit that the things they prohibit
0333     would tend to confuse a user trying to configure a module or a module developer
0334     writing the code to extract the parameters from a ParameterSet.  These rules
0335     tend to prohibit bad design.
0336 
0337     One strategy to avoid problems with wildcard parameters is to add a nested
0338     ParameterSet and put the wildcard parameters in the nested ParameterSet.
0339     The names and types in a nested ParameterSet will not interfere with names
0340     in the containing ParameterSet.
0341     */
0342     void checkAndGetLabelsAndTypes(std::set<std::string>& usedLabels,
0343                                    std::set<ParameterTypes>& parameterTypes,
0344                                    std::set<ParameterTypes>& wildcardTypes) const {
0345       checkAndGetLabelsAndTypes_(usedLabels, parameterTypes, wildcardTypes);
0346     }
0347 
0348     virtual bool isWildcard() const { return false; }
0349     static void printSpaces(std::ostream& os, int n);
0350 
0351   protected:
0352     virtual void checkAndGetLabelsAndTypes_(std::set<std::string>& usedLabels,
0353                                             std::set<ParameterTypes>& parameterTypes,
0354                                             std::set<ParameterTypes>& wildcardTypes) const = 0;
0355 
0356     virtual void validate_(ParameterSet& pset, std::set<std::string>& validatedLabels, Modifier modifier) const = 0;
0357 
0358     virtual void writeCfi_(std::ostream& os,
0359                            Modifier modifier,
0360                            bool& startWithComma,
0361                            int indentation,
0362                            CfiOptions&,
0363                            bool& wroteSomething) const = 0;
0364 
0365     virtual void print_(std::ostream&, Modifier /*modifier*/, bool /*writeToCfi*/, DocFormatHelper&) const {}
0366 
0367     virtual bool hasNestedContent_() const { return false; }
0368 
0369     virtual void printNestedContent_(std::ostream&, bool /*optional*/, DocFormatHelper&) const {}
0370 
0371     virtual bool exists_(ParameterSet const& pset) const = 0;
0372 
0373     virtual bool partiallyExists_(ParameterSet const& pset) const = 0;
0374 
0375     virtual int howManyXORSubNodesExist_(ParameterSet const& pset) const = 0;
0376 
0377     std::string comment_;
0378   };
0379 
0380   template <>
0381   struct value_ptr_traits<ParameterDescriptionNode> {
0382     static ParameterDescriptionNode* clone(ParameterDescriptionNode const* p) { return p->clone(); }
0383     static void destroy(ParameterDescriptionNode* p) { delete p; }
0384   };
0385 
0386   // operator>> ---------------------------------------------
0387 
0388   std::unique_ptr<ParameterDescriptionCases<bool>> operator>>(bool caseValue, ParameterDescriptionNode const& node);
0389 
0390   std::unique_ptr<ParameterDescriptionCases<int>> operator>>(int caseValue, ParameterDescriptionNode const& node);
0391 
0392   std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(std::string const& caseValue,
0393                                                                      ParameterDescriptionNode const& node);
0394 
0395   std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(char const* caseValue,
0396                                                                      ParameterDescriptionNode const& node);
0397 
0398   std::unique_ptr<ParameterDescriptionCases<bool>> operator>>(bool caseValue,
0399                                                               std::unique_ptr<ParameterDescriptionNode> node);
0400 
0401   std::unique_ptr<ParameterDescriptionCases<int>> operator>>(int caseValue,
0402                                                              std::unique_ptr<ParameterDescriptionNode> node);
0403 
0404   std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(std::string const& caseValue,
0405                                                                      std::unique_ptr<ParameterDescriptionNode> node);
0406 
0407   std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(char const* caseValue,
0408                                                                      std::unique_ptr<ParameterDescriptionNode> node);
0409 
0410   // operator&& ---------------------------------------------
0411 
0412   std::unique_ptr<ParameterDescriptionNode> operator&&(ParameterDescriptionNode const& node_left,
0413                                                        ParameterDescriptionNode const& node_right);
0414 
0415   std::unique_ptr<ParameterDescriptionNode> operator&&(std::unique_ptr<ParameterDescriptionNode> node_left,
0416                                                        ParameterDescriptionNode const& node_right);
0417 
0418   std::unique_ptr<ParameterDescriptionNode> operator&&(ParameterDescriptionNode const& node_left,
0419                                                        std::unique_ptr<ParameterDescriptionNode> node_right);
0420 
0421   std::unique_ptr<ParameterDescriptionNode> operator&&(std::unique_ptr<ParameterDescriptionNode> node_left,
0422                                                        std::unique_ptr<ParameterDescriptionNode> node_right);
0423 
0424   // operator|| ---------------------------------------------
0425 
0426   std::unique_ptr<ParameterDescriptionNode> operator||(ParameterDescriptionNode const& node_left,
0427                                                        ParameterDescriptionNode const& node_right);
0428 
0429   std::unique_ptr<ParameterDescriptionNode> operator||(std::unique_ptr<ParameterDescriptionNode> node_left,
0430                                                        ParameterDescriptionNode const& node_right);
0431 
0432   std::unique_ptr<ParameterDescriptionNode> operator||(ParameterDescriptionNode const& node_left,
0433                                                        std::unique_ptr<ParameterDescriptionNode> node_right);
0434 
0435   std::unique_ptr<ParameterDescriptionNode> operator||(std::unique_ptr<ParameterDescriptionNode> node_left,
0436                                                        std::unique_ptr<ParameterDescriptionNode> node_right);
0437 
0438   // operator^  ---------------------------------------------
0439 
0440   std::unique_ptr<ParameterDescriptionNode> operator^(ParameterDescriptionNode const& node_left,
0441                                                       ParameterDescriptionNode const& node_right);
0442 
0443   std::unique_ptr<ParameterDescriptionNode> operator^(std::unique_ptr<ParameterDescriptionNode> node_left,
0444                                                       ParameterDescriptionNode const& node_right);
0445 
0446   std::unique_ptr<ParameterDescriptionNode> operator^(ParameterDescriptionNode const& node_left,
0447                                                       std::unique_ptr<ParameterDescriptionNode> node_right);
0448 
0449   std::unique_ptr<ParameterDescriptionNode> operator^(std::unique_ptr<ParameterDescriptionNode> node_left,
0450                                                       std::unique_ptr<ParameterDescriptionNode> node_right);
0451 }  // namespace edm
0452 #endif