[glibmm/wip/dboles/ustring-sprintf: 6/6] ustring: Add sprintf(), wrapping g_strdup_printf()
- From: Daniel Boles <dboles src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glibmm/wip/dboles/ustring-sprintf: 6/6] ustring: Add sprintf(), wrapping g_strdup_printf()
- Date: Mon, 17 Dec 2018 01:02:15 +0000 (UTC)
commit c29764833525450bb3e8e2badcea08e4bc511323
Author: Daniel Boles <dboles src gnome org>
Date: Mon Dec 17 00:59:06 2018 +0000
ustring: Add sprintf(), wrapping g_strdup_printf()
Add another way to produce formatted ustrings, this time using printf
syntax, by forwarding arguments to g_strdup_printf() and then copying
the result into the returned ustring.
This includes a private ustring::sprintify() function that by default
just forward its argument but can be overloaded to do something else.
In this commit, that is overloaded for ustring and std::string so that
their .c_str() is passed to printf instead, avoiding the ugliness of
users always having to write .c_str() in their own lists of arguments.
Note that the same lack of type safety as plagues printf() and all its
variants (in both C and GLib) applies here: the arguments are just
forwarded on, so if you include too few or the wrong types for the
placeholders you specify, you invoke undefined behaviour just as in C.
https://gitlab.gnome.org/GNOME/glibmm/issues/21
glib/glibmm/ustring.h | 88 ++++++++++++++++++++++++++++++++++--
tests/Makefile.am | 2 +
tests/glibmm_ustring_sprintf/main.cc | 55 ++++++++++++++++++++++
3 files changed, 142 insertions(+), 3 deletions(-)
---
diff --git a/glib/glibmm/ustring.h b/glib/glibmm/ustring.h
index edd713db..f3c55e3c 100644
--- a/glib/glibmm/ustring.h
+++ b/glib/glibmm/ustring.h
@@ -192,9 +192,11 @@ gunichar get_unichar_from_std_iterator(std::string::const_iterator pos) G_GNUC_P
*
* @par Formatted output and internationalization
* @par
- * The methods ustring::compose() and ustring::format() provide a convenient
- * and powerful alternative to string streams, as shown in the example below.
- * Refer to the method documentation of compose() and format() for details.
+ * The methods ustring::compose(), ustring::format(), and
+ * ustring::sprintf() provide a convenient and powerful alternative to
+ * string streams, as shown in the example below. Refer to those methods’
+ * documentation for details.
+ *
* @code
* using Glib::ustring;
*
@@ -714,6 +716,43 @@ public:
template <class... Ts>
static inline ustring format(const Ts&... args);
+ /*! Substitute placeholders in a format string with the referenced arguments.
+ *
+ * This function takes a template string in the format used by C’s
+ * <tt>printf()</tt> family of functions and an arbitrary number of arguments,
+ * replaces each placeholder in the template with the formatted version of its
+ * corresponding argument, and returns the result in a new Glib::ustring.
+ *
+ * Note: It is the responsibility of the user to pass the correct number and
+ * types of arguments, as when calling <tt>printf()</tt> directly. glibmm does
+ * not, and cannot, check this. Failure to do this invokes undefined behavior.
+ *
+ * @par Example:
+ * @code
+ *
+ * const auto name = Glib::ustring{"Dennis"};
+ * const auto your_cows = 3;
+ * const auto my_cows = 11;
+ * const auto cow_percentage = 100.0 * your_cows / my_cows;
+ *
+ * const auto text = Glib::ustring::sprintf(
+ * "Hi, %s! You have %d cows. That's about %0.2f%% of the %d cows I have.",
+ * name, your_cows, cow_percentage, my_cows);
+ *
+ * std::cout << text;
+ * // Hi, Dennis! You have 2 cows. That's about 27.27% of the 11 cows I have.
+ * @endcode
+ *
+ * @param fmt The template string, in the format used by <tt>printf()</tt> et al.
+ * @param args A set of arguments having the number and types required by @a fmt.
+ *
+ * @return The substituted message string.
+ *
+ * @newin{2,56}
+ */
+ template <class... Ts>
+ static inline ustring sprintf(const ustring& fmt, const Ts&... args);
+
//! @}
private:
@@ -740,6 +779,10 @@ private:
class FormatStream;
+ template<class T> static inline const T& sprintify(const T& arg);
+ static inline const char* sprintify(const ustring& arg);
+ static inline const char* sprintify(const std::string& arg);
+
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
std::string string_;
@@ -1156,6 +1199,33 @@ public:
inline const ustring& ref() const { return string_; }
};
+/* These helper functions used by ustring::sprintf() let users pass C++ strings
+ * to match %s placeholders, without the hassle of writing .c_str() in user code
+ */
+template<typename T>
+inline // static
+ const T&
+ ustring::sprintify(const T& arg)
+{
+ return arg;
+}
+
+inline // static
+ const char*
+ ustring::sprintify(const ustring& arg)
+{
+ return arg.c_str();
+}
+
+inline // static
+ const char*
+ ustring::sprintify(const std::string& arg)
+{
+ return arg.c_str();
+}
+
+// Public methods
+
inline // static
ustring
ustring::compose(const ustring& fmt)
@@ -1174,6 +1244,18 @@ inline // static
return compose_private(fmt, {&Stringify<Ts>(args).ref()...});
}
+template <class... Ts>
+inline // static
+ ustring
+ ustring::sprintf(const ustring& fmt, const Ts&... args)
+{
+ auto c_str = g_strdup_printf(fmt.c_str(), sprintify(args)...);
+ Glib::ustring ustr(c_str);
+ g_free(c_str);
+
+ return ustr;
+}
+
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/** @relates Glib::ustring */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 339eeada..3f6f81c9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,6 +40,7 @@ check_PROGRAMS = \
glibmm_objectbase_move/test \
glibmm_ustring_compose/test \
glibmm_ustring_format/test \
+ glibmm_ustring_sprintf/test \
glibmm_value/test \
glibmm_variant/test \
glibmm_vector/test \
@@ -113,6 +114,7 @@ glibmm_objectbase_move_test_SOURCES = glibmm_objectbase_move/main.cc \
glibmm_object/test_derived_object.h
glibmm_ustring_compose_test_SOURCES = glibmm_ustring_compose/main.cc
glibmm_ustring_format_test_SOURCES = glibmm_ustring_format/main.cc
+glibmm_ustring_sprintf_test_SOURCES = glibmm_ustring_sprintf/main.cc
glibmm_value_test_SOURCES = glibmm_value/main.cc
glibmm_variant_test_SOURCES = glibmm_variant/main.cc
glibmm_vector_test_SOURCES = glibmm_vector/main.cc
diff --git a/tests/glibmm_ustring_sprintf/main.cc b/tests/glibmm_ustring_sprintf/main.cc
new file mode 100644
index 00000000..232be152
--- /dev/null
+++ b/tests/glibmm_ustring_sprintf/main.cc
@@ -0,0 +1,55 @@
+#include <glibmm/init.h>
+#include <glibmm/ustring.h>
+
+#include <cstdlib>
+#include <iostream>
+
+namespace {
+
+template <class... Ts>
+void
+test(const Glib::ustring& expected, const Glib::ustring& fmt, const Ts&... ts)
+{
+ const auto actual = Glib::ustring::sprintf(fmt, ts...);
+
+ if (actual != expected)
+ {
+ std::cerr << "error testing Glib::ustring::sprintf():\n"
+ "expected (" << expected.size() << "):\n" << expected << "\n\n"
+ "actual (" << actual .size() << "):\n" << actual << "\n";
+
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+} // anonymous namespace
+
+int
+main(int, char**)
+{
+ // Don't use the user's preferred locale. The decimal delimiter may be ','
+ // instead of the expected '.'.
+ Glib::set_init_to_users_preferred_locale(false);
+
+ Glib::init();
+
+ test("No formatting here, just a boring string",
+ "No formatting here, just a boring string");
+
+ test("Interpolating another string: \"here it is\" and there it was gone.",
+ "Interpolating another string: \"%s\" and there it was gone.", "here it is");
+
+ test("some stuff and then an int: 42",
+ "some stuff and then an int: %d", 42);
+
+ const auto greeting = std::string{"Hi"};
+ const auto name = Glib::ustring{"Dennis"};
+ const auto your_cows = 3;
+ const auto my_cows = 11;
+ const auto cow_percentage = 100.0 * your_cows / my_cows;
+ test("Hi, Dennis! You have 3 cows.\nThat's about 27.27% of the 11 cows I have.",
+ "%s, %s! You have %d cows.\nThat's about %0.2f%% of the %d cows I have.",
+ greeting, name, your_cows, cow_percentage, my_cows);
+
+ return EXIT_SUCCESS;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]