}
}
-/*! \brief Helper function for writing a string of GPU IDs.
- *
- * \param[in] ids A container of integer GPU IDs
- * \return A comma-separated string of GPU IDs */
-template <typename Container>
-static std::string makeGpuIdsString(const Container &ids)
-{
- std::string output;
-
- if (0 != ids.size())
- {
- typename Container::const_iterator it = ids.begin();
- output += gmx::formatString("%d", *it);
- for (++it; it != ids.end(); ++it)
- {
- output += gmx::formatString(",%d", *it);
- }
- }
- return output;
-}
-
/*! \brief Helper function for reporting GPU usage information
* in the mdrun log file
*
{
// gpu_opt->cuda_dev_compatible is only populated during auto-selection
std::string gpuIdsString =
- makeGpuIdsString(gmx::ConstArrayRef<int>(gpu_opt->cuda_dev_compatible,
- gpu_opt->cuda_dev_compatible +
- gpu_opt->ncuda_dev_compatible));
+ formatAndJoin(gmx::constArrayRefFromArray(gpu_opt->cuda_dev_compatible,
+ gpu_opt->ncuda_dev_compatible),
+ ",", gmx::StringFormatter("%d"));
bool bPluralGpus = gpu_opt->ncuda_dev_compatible > 1;
output += gmx::formatString("%d compatible GPU%s %s present, with ID%s %s\n",
gpu_opt->ncuda_dev_compatible,
{
gpuIdsInUse.push_back(get_gpu_device_id(gpu_info, gpu_opt, i));
}
- std::string gpuIdsString = makeGpuIdsString(gpuIdsInUse);
+ std::string gpuIdsString =
+ formatAndJoin(gpuIdsInUse, ",", gmx::StringFormatter("%d"));
int numGpusInUse = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
bool bPluralGpus = numGpusInUse > 1;
*/
std::string formatString(const char *fmt, ...);
-/*! \brief
- * Splits a string to whitespace separated tokens.
+/*! \brief Function object that wraps a call to formatString() that
+ * expects a single conversion argument, for use with algorithms. */
+class StringFormatter
+{
+ public:
+ /*! \brief Constructor
+ *
+ * \param[in] format The printf-style format string that will
+ * be applied to convert values of type T to
+ * string. Exactly one argument to the conversion
+ * specification(s) in `format` is supported. */
+ explicit StringFormatter(const char *format) : format_(format)
+ {
+ }
+
+ //! Implements the formatting functionality
+ template <typename T>
+ std::string operator()(const T &value) const
+ {
+ return formatString(format_, value);
+ }
+
+ private:
+ //! Format string to use
+ const char *format_;
+};
+
+/*! \brief Function object to implement the same interface as
+ * `StringFormatter` to use with strings that should not be formatted
+ * further. */
+class IdentityFormatter
+{
+ public:
+ //! Implements the formatting non-functionality
+ std::string operator()(const std::string &value) const
+ {
+ return value;
+ }
+};
+
+/*! \brief Formats all the range as strings, and then joins them with
+ * a separator in between.
*
- * \param[in] str String to process.
- * \returns \p str split into tokens at each whitespace sequence.
+ * \param[in] begin Iterator the beginning of the range to join.
+ * \param[in] end Iterator the end of the range to join.
+ * \param[in] separator String to put in between the joined strings.
+ * \param[in] formatter Function object to format the objects in
+ * `container` as strings
+ * \returns All objects in the range from `begin` to `end` formatted
+ * as strings and concatenated with `separator` between each pair.
* \throws std::bad_alloc if out of memory.
+ */
+template <typename InputIterator, typename FormatterType>
+std::string formatAndJoin(InputIterator begin, InputIterator end, const char *separator, const FormatterType &formatter)
+{
+ std::string result;
+ const char *currentSeparator = "";
+ for (InputIterator i = begin; i != end; ++i)
+ {
+ result.append(currentSeparator);
+ result.append(formatter(*i));
+ currentSeparator = separator;
+ }
+ return result;
+}
+
+/*! \brief Formats all elements of the container as strings, and then
+ * joins them with a separator in between.
*
- * This function works like `split` in Python, i.e., leading and trailing
- * whitespace is ignored, and consecutive whitespaces are treated as a single
- * separator.
+ * \param[in] container Objects to join.
+ * \param[in] separator String to put in between the joined strings.
+ * \param[in] formatter Function object to format the objects in
+ * `container` as strings
+ * \returns All objects from `container` formatted as strings and
+ * concatenated with `separator` between each pair.
+ * \throws std::bad_alloc if out of memory.
*/
-std::vector<std::string> splitString(const std::string &str);
+template <typename ContainerType, typename FormatterType>
+std::string formatAndJoin(const ContainerType &container, const char *separator, const FormatterType &formatter)
+{
+ return formatAndJoin(container.begin(), container.end(), separator, formatter);
+}
/*! \brief
* Joins strings from a range with a separator in between.
std::string joinStrings(InputIterator begin, InputIterator end,
const char *separator)
{
- std::string result;
- const char *currentSeparator = "";
- for (InputIterator i = begin; i != end; ++i)
- {
- result.append(currentSeparator);
- result.append(*i);
- currentSeparator = separator;
- }
- return result;
+ return formatAndJoin(begin, end, separator, IdentityFormatter());
}
+
/*! \brief
* Joins strings from a container with a separator in between.
*
return concatenateStrings(sarray, count);
}
+/*! \brief
+ * Splits a string to whitespace separated tokens.
+ *
+ * \param[in] str String to process.
+ * \returns \p str split into tokens at each whitespace sequence.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * This function works like `split` in Python, i.e., leading and trailing
+ * whitespace is ignored, and consecutive whitespaces are treated as a single
+ * separator.
+ */
+std::vector<std::string> splitString(const std::string &str);
+
/*! \brief
* Replace all occurrences of a string with another string.
*
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "gromacs/utility/arrayref.h"
+
#include "testutils/refdata.h"
#include "testutils/stringtest.h"
EXPECT_EQ("x10", longString.substr(1999));
}
+/********************************************************************
+ * Tests for StringFormatter
+ */
+
+TEST(StringFormatterTest, HandlesBasicFormatting)
+{
+ int value = 103;
+ EXPECT_EQ("103", gmx::StringFormatter("%d") (value));
+ EXPECT_EQ("null", gmx::StringFormatter("null") (value));
+}
+
+/********************************************************************
+ * Tests for formatAndJoin
+ */
+
+TEST(formatAndJoinTest, Works)
+{
+ const char * const words[] = { "The", "quick", "brown", "fox" };
+ EXPECT_EQ("The .quick .brown .fox ",
+ gmx::formatAndJoin(gmx::ConstArrayRef<const char *>(words), ".",
+ gmx::StringFormatter("%-10s")));
+
+ const int values[] = { 0, 1, 4 };
+ EXPECT_EQ("0,1,4", gmx::formatAndJoin(gmx::ConstArrayRef<int>(values), ",",
+ gmx::StringFormatter("%d")));
+}
+
+/********************************************************************
+ * Tests for joinStrings
+ */
+
+TEST(JoinStringsTest, Works)
+{
+ const char * const words[] = { "The", "quick", "brown", "fox" };
+ gmx::ConstArrayRef<const char *> refToWords(words);
+ EXPECT_EQ("The; quick; brown; fox", gmx::joinStrings(refToWords.begin(), refToWords.end(), "; "));
+ EXPECT_EQ("The-quick-brown-fox", gmx::joinStrings(refToWords, "-"));
+}
+
/********************************************************************
* Tests for concatenateStrings()
*/