text: /* Copyright (c) 2016 The Khronos Group Inc.
   * Copyright (c) 2016 Valve Corporation
   * Copyright (c) 2016 LunarG, Inc.
   * Copyright (c) 2016 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  #ifndef PARAMETER_NAME_H
  #define PARAMETER_NAME_H
  
  #include <cassert>
  #include <sstream>
  #include <string>
  #include <vector>
  
  /**
   * Parameter name string supporting deferred formatting for array subscripts.
   *
   * Custom parameter name class with support for deferred formatting of names containing array subscripts.  The class stores
   * a format string and a vector of index values, and performs string formatting when an accessor function is called to
   * retrieve the name string.  This class was primarily designed to be used with validation functions that receive a parameter name
   * string and value as arguments, and print an error message that includes the parameter name when the value fails a validation
   * test.  Using standard strings with these validation functions requires that parameter names containing array subscripts be
   * formatted before each validation function is called, performing the string formatting even when the value passes validation
   * and the string is not used:
   *         sprintf(name, "pCreateInfo[%d].sType", i);
   *         validate_stype(name, pCreateInfo[i].sType);
   *
   * With the ParameterName class, a format string and a vector of format values are stored by the ParameterName object that is
   * provided to the validation function.  String formatting is then performed only when the validation function retrieves the
   * name string from the ParameterName object:
   *         validate_stype(ParameterName("pCreateInfo[%i].sType", IndexVector{ i }), pCreateInfo[i].sType);
   */
  class ParameterName {
    public:
      /// Container for index values to be used with parameter name string formatting.
      typedef std::vector<size_t> IndexVector;
  
      /// Format specifier for the parameter name string, to be replaced by an index value.  The parameter name string must contain
      /// one format specifier for each index value specified.
      const std::string IndexFormatSpecifier = "%i";
  
    public:
      /**
       * Construct a ParameterName object from a string literal, without formatting.
       *
       * @param source Paramater name string without format specifiers.
       *
       * @pre The source string must not contain the %i format specifier.
       */
      ParameterName(const char *source) : source_(source) { assert(IsValid()); }
  
      /**
      * Construct a ParameterName object from a std::string object, without formatting.
      *
      * @param source Paramater name string without format specifiers.
      *
      * @pre The source string must not contain the %i format specifier.
      */
      ParameterName(const std::string &source) : source_(source) { assert(IsValid()); }
  
      /**
      * Construct a ParameterName object from a std::string object, without formatting.
      *
      * @param source Paramater name string without format specifiers.
      *
      * @pre The source string must not contain the %i format specifier.
      */
      ParameterName(const std::string &&source) : source_(std::move(source)) { assert(IsValid()); }
  
      /**
      * Construct a ParameterName object from a std::string object, with formatting.
      *
      * @param source Paramater name string with format specifiers.
      * @param args Array index values to be used for formatting.
      *
      * @pre The number of %i format specifiers contained by the source string must match the number of elements contained
      *      by the index vector.
      */
      ParameterName(const std::string &source, const IndexVector &args) : source_(source), args_(args) { assert(IsValid()); }
  
      /**
      * Construct a ParameterName object from a std::string object, with formatting.
      *
      * @param source Paramater name string with format specifiers.
      * @param args Array index values to be used for formatting.
      *
      * @pre The number of %i format specifiers contained by the source string must match the number of elements contained
      *      by the index vector.
      */
      ParameterName(const std::string &&source, const IndexVector &&args) : source_(std::move(source)), args_(std::move(args)) {
          assert(IsValid());
      }
  
      /// Retrive the formatted name string.
      std::string get_name() const { return (args_.empty()) ? source_ : Format(); }
  
    private:
      /// Replace the %i format specifiers in the source string with the values from the index vector.
      std::string Format() const {
          std::string::size_type current = 0;
          std::string::size_type last = 0;
          std::stringstream format;
  
          for (size_t index : args_) {
              current = source_.find(IndexFormatSpecifier, last);
              if (current == std::string::npos) {
                  break;
              }
              format << source_.substr(last, (current - last)) << index;
              last = current + IndexFormatSpecifier.length();
          }
  
          format << source_.substr(last, std::string::npos);
  
          return format.str();
      }
  
      /// Check that the number of %i format specifiers in the source string matches the number of elements in the index vector.
      bool IsValid() {
          // Count the number of occurances of the format specifier
          uint32_t count = 0;
          std::string::size_type pos = source_.find(IndexFormatSpecifier);
  
          while (pos != std::string::npos) {
              ++count;
              pos = source_.find(IndexFormatSpecifier, pos + 1);
          }
  
          return (count == args_.size());
      }
  
    private:
      std::string source_; ///< Format string.
      IndexVector args_;   ///< Array index values for formatting.
  };
  
  #endif // PARAMETER_NAME_H
  