ClassFile

Comment

NodeGuard

ParameterDescriptionNode

ParameterModifier

ParameterTypeToEnum

ParameterTypes

Paths

Typed

Untyped

value_ptr_traits

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
#ifndef FWCore_ParameterSet_ParameterDescriptionNode_h
#define FWCore_ParameterSet_ParameterDescriptionNode_h

// This is a base class for the class that describes
// the parameters that are allowed or required to be
// in a ParameterSet.  It is also a base class for
// other more complex logical structures which describe
// which combinations of parameters are allowed to be
// in a ParameterSet.

#include "FWCore/Utilities/interface/value_ptr.h"

#include <string>
#include <set>
#include <iosfwd>
#include <memory>
#include <variant>
#include <optional>
#include <unordered_map>
#include <vector>
#include <cassert>

namespace edm {

  class ParameterSet;
  template <typename T>
  class ParameterDescriptionCases;
  class DocFormatHelper;

  // Originally these were defined such that the values were the
  // same as in the ParameterSet Entry class and the validation
  // depended on that.  But at the moment I'm typing this comment,
  // the code no longer depends on the values being the same (which
  // is probably good because nothing enforces the correspondence,
  // a task for the future when someone has free time would be
  // to define the values in a common header, but that would involve
  // significant changes to ParameterSet ...)
  enum ParameterTypes {
    k_int32 = 'I',
    k_vint32 = 'i',
    k_uint32 = 'U',
    k_vuint32 = 'u',
    k_int64 = 'L',
    k_vint64 = 'l',
    k_uint64 = 'X',
    k_vuint64 = 'x',
    k_double = 'D',
    k_vdouble = 'd',
    k_bool = 'B',
    k_stringRaw = 'Z',
    k_vstringRaw = 'z',
    k_stringHex = 'S',
    k_vstringHex = 's',
    k_EventID = 'E',
    k_VEventID = 'e',
    k_LuminosityBlockID = 'M',
    k_VLuminosityBlockID = 'm',
    k_InputTag = 't',
    k_VInputTag = 'v',
    k_ESInputTag = 'g',
    k_VESInputTag = 'G',
    k_FileInPath = 'F',
    k_LuminosityBlockRange = 'A',
    k_VLuminosityBlockRange = 'a',
    k_EventRange = 'R',
    k_VEventRange = 'r',
    k_PSet = 'Q',
    k_VPSet = 'q'
  };

  std::string parameterTypeEnumToString(ParameterTypes iType);

  enum class ParameterModifier : unsigned char { kNone, kOptional, kObsolete };
  inline ParameterModifier modifierIsOptional(bool iOptional) {
    return iOptional ? ParameterModifier::kOptional : ParameterModifier::kNone;
  }

  namespace cfi {
    struct Paths {
      //This is the 'path' through the cms.PSet hierarchy.
      // E.g. foo.bar.tar would have a Path for foo, bar, and tar with
      // tar having a null nodes_ variable.
      std::optional<std::unordered_map<std::string, Paths>> nodes_;
    };
    struct Typed {};
    struct ClassFile {
      void parameterMustBeTyped() {
        Paths* p = &fullPaths_;
        for (auto n : presentPath_) {
          if (not p->nodes_) {
            p->nodes_ = std::unordered_map<std::string, Paths>();
          }
          p = &(p->nodes_.value().emplace(n, Paths{})).first->second;
        }
      }
      void pushNode(std::string_view iNode) { presentPath_.push_back(iNode); }
      void popNode() {
        assert(not presentPath_.empty());
        presentPath_.pop_back();
      }

      Paths releasePaths() { return std::move(fullPaths_); }

    private:
      std::vector<std::string_view> presentPath_;
      Paths fullPaths_;
    };
    struct Untyped {
      Untyped(Paths iPaths) : paths_(iPaths) {}
      bool needToSwitchToTyped(std::string_view iNode) {
        presentPath_.push_back(iNode);
        if (not paths_.nodes_.has_value()) {
          return false;
        }
        const Paths* p = &paths_;
        for (auto const& n : presentPath_) {
          if (not paths_.nodes_.has_value()) {
            return false;
          }
          auto f = paths_.nodes_->find(std::string(n));
          if (f == paths_.nodes_->end()) {
            return false;
          }
          p = &f->second;
        }
        return not p->nodes_.has_value();
      }
      void popNode() {
        assert(not presentPath_.empty());
        presentPath_.pop_back();
      }

    private:
      std::vector<std::string_view> presentPath_;
      Paths paths_;
    };

    using CfiOptions = std::variant<cfi::Typed, cfi::ClassFile, cfi::Untyped>;

    inline void parameterMustBeTyped(CfiOptions& iOps) noexcept {
      if (std::holds_alternative<cfi::ClassFile>(iOps)) {
        std::get<cfi::ClassFile>(iOps).parameterMustBeTyped();
      }
    }
    inline void parameterMustBeTyped(CfiOptions& iOps, std::string_view iNode) noexcept {
      if (std::holds_alternative<cfi::ClassFile>(iOps)) {
        auto& d = std::get<cfi::ClassFile>(iOps);
        d.pushNode(iNode);
        d.parameterMustBeTyped();
        d.popNode();
      }
    }
    [[nodiscard]] inline bool shouldWriteUntyped(CfiOptions const& iOps) noexcept {
      return std::holds_alternative<cfi::Untyped>(iOps);
    }

    struct NodeGuard {
      NodeGuard(CfiOptions& iOp) : options_(&iOp) {}
      NodeGuard() = delete;
      NodeGuard(NodeGuard const&) = delete;
      NodeGuard& operator=(NodeGuard const&) = delete;
      NodeGuard(NodeGuard&& iOther) : options_{iOther.options_} { iOther.options_ = nullptr; }
      NodeGuard& operator=(NodeGuard&& iOther) {
        NodeGuard temp{std::move(iOther)};
        options_ = temp.options_;
        temp.options_ = nullptr;
        return *this;
      }
      ~NodeGuard() {
        if (nullptr == options_) {
          return;
        }
        if (std::holds_alternative<ClassFile>(*options_)) {
          std::get<ClassFile>(*options_).popNode();
        } else if (std::holds_alternative<Untyped>(*options_)) {
          std::get<Untyped>(*options_).popNode();
        }
      }
      CfiOptions* options_;
    };

    [[nodiscard]] inline std::pair<bool, NodeGuard> needToSwitchToTyped(std::string_view iNode,
                                                                        CfiOptions& iOpt) noexcept {
      if (std::holds_alternative<Untyped>(iOpt)) {
        return std::pair(std::get<Untyped>(iOpt).needToSwitchToTyped(iNode), NodeGuard(iOpt));
      } else if (std::holds_alternative<ClassFile>(iOpt)) {
        std::get<ClassFile>(iOpt).pushNode(iNode);
      }
      return std::pair(false, NodeGuard(iOpt));
    }
  }  // namespace cfi
  using CfiOptions = cfi::CfiOptions;

  struct ParameterTypeToEnum {
    template <class T>
    static ParameterTypes toEnum();
  };

  class Comment {
  public:
    Comment();
    explicit Comment(std::string const& iComment);
    explicit Comment(char const* iComment);
    std::string const& comment() const { return comment_; }

  private:
    std::string comment_;
  };

  class ParameterDescriptionNode {
  public:
    using Modifier = ParameterModifier;

    ParameterDescriptionNode() {}

    explicit ParameterDescriptionNode(Comment const& iComment) : comment_(iComment.comment()) {}

    virtual ~ParameterDescriptionNode();

    virtual ParameterDescriptionNode* clone() const = 0;

    std::string const& comment() const { return comment_; }
    void setComment(std::string const& value);
    void setComment(char const* value);

    // The validate function should do one of three things, find that the
    // node "exists", make the node "exist" by inserting missing parameters
    // or throw.  The only exception to this rule occurs when the argument
    // named "modifier" is kOptional or kObsolete, which should only be possible for the
    // top level nodes of a ParameterSetDescription.  When a parameter is
    // found or inserted its label is added into the list of validatedLabels.
    void validate(ParameterSet& pset, std::set<std::string>& validatedLabels, Modifier modifier) const {
      validate_(pset, validatedLabels, modifier);
    }

    // As long as it has default values, this will attempt to write
    // parameters associated with a node into a cfi file that is
    // being automatically generated.  It is quite possible for
    // to produce a cfi that will fail validation.  In some cases,
    // this will imply the user is required to supply certain missing
    // parameters that do not appear in the cfi and do not have defaults
    // in the description.  It is also possible to create a pathological
    // ParameterSetDescription where the algorithm fails to write
    // a valid cfi, in some cases the description can be so pathological
    // that it is impossible to write a cfi that will pass validation.
    void writeCfi(std::ostream& os,
                  Modifier modifier,
                  bool& startWithComma,
                  int indentation,
                  CfiOptions& options,
                  bool& wroteSomething) const {
      writeCfi_(os, modifier, startWithComma, indentation, options, wroteSomething);
    }

    // Print out the description in human readable format
    void print(std::ostream& os, Modifier modifier, bool writeToCfi, DocFormatHelper& dfh) const;

    bool hasNestedContent() const { return hasNestedContent_(); }

    void printNestedContent(std::ostream& os, bool optional, DocFormatHelper& dfh) const;

    // The next three functions are only called by the logical nodes
    // on their subnodes.  When executing these functions, the
    // insertion of missing parameters does not occur.

    // Usually checks to see if a parameter exists in the configuration, but
    // if the node is a logical node, then it returns the value of the logical
    // expression.
    bool exists(ParameterSet const& pset) const { return exists_(pset); }

    // For most nodes, this simply returns the same value as the exists
    // function.  But for AND nodes this returns true if either its subnodes
    // exists.  Used by operator&& during validation, if either of an AND node's
    // subnodes exists, then both subnodes get validated.
    bool partiallyExists(ParameterSet const& pset) const { return partiallyExists_(pset); }

    // For most nodes, this simply returns the same value as the exists
    // function. It is different for an XOR node.  It counts
    // XOR subnodes whose exists function returns true.  And it
    // does this recursively into XOR nodes that are contained in
    // other XOR nodes.
    // Used by operator^ during validation:
    // -- if it returns more than 1, then validation will throw,
    // -- if it returns exactly one, then only the nonzero subnode gets validated
    // -- if it returns zero, then validation tries to validate the first node and
    // then rechecks to see what the missing parameter insertion did (there could
    // be side effects on the nodes that were not validated)
    int howManyXORSubNodesExist(ParameterSet const& pset) const { return howManyXORSubNodesExist_(pset); }

    /* Validation puts requirements on which parameters can and cannot exist
    within a ParameterSet.  The evaluation of whether a ParameterSet passes
    or fails the rules in the ParameterSetDescription is complicated by
    the fact that we allow missing parameters to be injected into the
    ParameterSet during validation.  One must worry whether injecting a
    missing parameter invalidates some other part of the ParameterSet that
    was already checked and determined to be OK.  The following restrictions
    avoid that problem.

        - The same parameter labels cannot occur in different nodes of the
        same ParameterSetDescription.  There are two exceptions to this.
        Nodes that are contained in the cases of a ParameterSwitch or the
        subnodes of an "exclusive or" are allowed to use the same labels.

        - If insertion is necessary to make an "exclusive or" node pass
        validation, then the insertion could make more than one of the
        possibilities evaluate true.  This must be checked for after the
        insertions occur. The behavior is to throw a Configuration exception
        if this problem is encountered. (Example: (A && B) ^ (A && C) where
        C already exists in the ParameterSet but A and B do not.  A and B
        get inserted by the algorithm, because it tries to make the first
        possibility true when all fail without insertion.  Then both
        parts of the "exclusive or" pass, which is a validation failure).

        - Another potential problem is that a parameter insertion related
        to one ParameterDescription could match unrelated wildcards causing
        other validation requirements to change from being passing to failing
        or vice versa.  This makes it almost impossible to determine if a
        ParameterSet passes validation.  Each time you try to loop through
        and check, the result of validation could change.  To avoid this problem,
        a list is maintained of the type for all wildcards.  Another list is
        maintained for the type of all parameters.  As new items are added
        we check for collisions.  The function that builds the ParameterSetDescription,
        will throw if this rule is violated.  At the moment, the criteria
        for a collision is matching types between a parameter and a wildcard.
        (This criteria is overrestrictive.  With some additional CPU and
        code development the restriction could be loosened to parameters that
        might be injected cannot match the type, trackiness, and wildcard label
        pattern of any wildcard that requires a match.  And further this
        could not apply to wildcards on different branches of a ParameterSwitch
        or "exclusive or".)

    These restrictions have the additional benefit that the things they prohibit
    would tend to confuse a user trying to configure a module or a module developer
    writing the code to extract the parameters from a ParameterSet.  These rules
    tend to prohibit bad design.

    One strategy to avoid problems with wildcard parameters is to add a nested
    ParameterSet and put the wildcard parameters in the nested ParameterSet.
    The names and types in a nested ParameterSet will not interfere with names
    in the containing ParameterSet.
    */
    void checkAndGetLabelsAndTypes(std::set<std::string>& usedLabels,
                                   std::set<ParameterTypes>& parameterTypes,
                                   std::set<ParameterTypes>& wildcardTypes) const {
      checkAndGetLabelsAndTypes_(usedLabels, parameterTypes, wildcardTypes);
    }

    virtual bool isWildcard() const { return false; }
    static void printSpaces(std::ostream& os, int n);

  protected:
    virtual void checkAndGetLabelsAndTypes_(std::set<std::string>& usedLabels,
                                            std::set<ParameterTypes>& parameterTypes,
                                            std::set<ParameterTypes>& wildcardTypes) const = 0;

    virtual void validate_(ParameterSet& pset, std::set<std::string>& validatedLabels, Modifier modifier) const = 0;

    virtual void writeCfi_(std::ostream& os,
                           Modifier modifier,
                           bool& startWithComma,
                           int indentation,
                           CfiOptions&,
                           bool& wroteSomething) const = 0;

    virtual void print_(std::ostream&, Modifier /*modifier*/, bool /*writeToCfi*/, DocFormatHelper&) const {}

    virtual bool hasNestedContent_() const { return false; }

    virtual void printNestedContent_(std::ostream&, bool /*optional*/, DocFormatHelper&) const {}

    virtual bool exists_(ParameterSet const& pset) const = 0;

    virtual bool partiallyExists_(ParameterSet const& pset) const = 0;

    virtual int howManyXORSubNodesExist_(ParameterSet const& pset) const = 0;

    std::string comment_;
  };

  template <>
  struct value_ptr_traits<ParameterDescriptionNode> {
    static ParameterDescriptionNode* clone(ParameterDescriptionNode const* p) { return p->clone(); }
    static void destroy(ParameterDescriptionNode* p) { delete p; }
  };

  // operator>> ---------------------------------------------

  std::unique_ptr<ParameterDescriptionCases<bool>> operator>>(bool caseValue, ParameterDescriptionNode const& node);

  std::unique_ptr<ParameterDescriptionCases<int>> operator>>(int caseValue, ParameterDescriptionNode const& node);

  std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(std::string const& caseValue,
                                                                     ParameterDescriptionNode const& node);

  std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(char const* caseValue,
                                                                     ParameterDescriptionNode const& node);

  std::unique_ptr<ParameterDescriptionCases<bool>> operator>>(bool caseValue,
                                                              std::unique_ptr<ParameterDescriptionNode> node);

  std::unique_ptr<ParameterDescriptionCases<int>> operator>>(int caseValue,
                                                             std::unique_ptr<ParameterDescriptionNode> node);

  std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(std::string const& caseValue,
                                                                     std::unique_ptr<ParameterDescriptionNode> node);

  std::unique_ptr<ParameterDescriptionCases<std::string>> operator>>(char const* caseValue,
                                                                     std::unique_ptr<ParameterDescriptionNode> node);

  // operator&& ---------------------------------------------

  std::unique_ptr<ParameterDescriptionNode> operator&&(ParameterDescriptionNode const& node_left,
                                                       ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator&&(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                       ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator&&(ParameterDescriptionNode const& node_left,
                                                       std::unique_ptr<ParameterDescriptionNode> node_right);

  std::unique_ptr<ParameterDescriptionNode> operator&&(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                       std::unique_ptr<ParameterDescriptionNode> node_right);

  // operator|| ---------------------------------------------

  std::unique_ptr<ParameterDescriptionNode> operator||(ParameterDescriptionNode const& node_left,
                                                       ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator||(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                       ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator||(ParameterDescriptionNode const& node_left,
                                                       std::unique_ptr<ParameterDescriptionNode> node_right);

  std::unique_ptr<ParameterDescriptionNode> operator||(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                       std::unique_ptr<ParameterDescriptionNode> node_right);

  // operator^  ---------------------------------------------

  std::unique_ptr<ParameterDescriptionNode> operator^(ParameterDescriptionNode const& node_left,
                                                      ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator^(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                      ParameterDescriptionNode const& node_right);

  std::unique_ptr<ParameterDescriptionNode> operator^(ParameterDescriptionNode const& node_left,
                                                      std::unique_ptr<ParameterDescriptionNode> node_right);

  std::unique_ptr<ParameterDescriptionNode> operator^(std::unique_ptr<ParameterDescriptionNode> node_left,
                                                      std::unique_ptr<ParameterDescriptionNode> node_right);
}  // namespace edm
#endif