layers: Add parameter name formatting class
authorDustin Graves <dustin@lunarg.com>
Fri, 22 Jul 2016 19:19:02 +0000 (13:19 -0600)
committerMark Lobodzinski <mark@lunarg.com>
Wed, 24 Aug 2016 19:20:40 +0000 (13:20 -0600)
Add a 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.

Change-Id: I941116a16b6fa19e63314cbeab33223e0fa072d3

layers/parameter_name.h [new file with mode: 0644]

diff --git a/layers/parameter_name.h b/layers/parameter_name.h
new file mode 100644 (file)
index 0000000..b788584
--- /dev/null
@@ -0,0 +1,148 @@
+/* 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