GNOME Bugzilla – Bug 585413
Glib::Value<std::string> specialization
Last modified: 2011-06-14 08:22:13 UTC
Please describe the problem: Note, this bug is probably also applicable to Glib::ustring specialization as well. The specialization setup for std::string does not account for the fact that an std::string can contain non printable characters, including the NUL character. Because of the way the specialization is handled, any std::string containing a NUL character loses all data after the first NUL character (hence the "Critical" bug status, ;-) ). In order to fix this, one would either have to modify glib, by either adding another method such as g_value_set_string_with_length( GValue *value, const gchar *v_string, const int v_length ), or just not depend on glib here (as an std::string clearly doesn't have a C primitive equivalent. cstrings don't support NUL characters as std::strings do, a reason why one would choose to use C++ and glibmm over C and glib). One could think of the current implementation as having a solution looking for a problem (that doesn't exist). This specialization isn't necessary, at least not for std::string, and definitely not for std::string as cstring. Perhaps specialize for char *string with g_value_set_string() as glib intended, and if necessary, specialize for std::string without crippling the it? relevant portion of value.cc: /**** Glib::Value<std::string> *********************************************/ void Value<std::string>::set(const std::string& data) { g_value_set_string(&gobject_, data.c_str()); } /**** Glib::Value<Glib::ustring> *******************************************/ void Value<Glib::ustring>::set(const Glib::ustring& data) { g_value_set_string(&gobject_, data.c_str()); } Steps to reproduce: std::string a; a.assign( "test\x0test", 9 ); cout << "a: " << a << " - " << a.length() << endl; Glib::Value<std::string> b; b.init( Glib::Value<std::string>::value_type() ); b.set( a ); cout << "b: " << b.get() << " - " << b.get().length() << endl; Actual results: a: test test - 9 b: test - 4 Expected results: a: test test - 9 b: test test - 9 Does this happen every time? yes Other information:
I guess giving a bit more background as to how this is an issue would might be of use as well. I ran into this issue when using the Gtk::TreeView and Gtk::TreeModel architecture. Gtk::TreeModelColumn uses Glib::Value (as probably do many gnome libraries), thus when using a model type of std::string, if the std::string contains a NUL character, then data is lost from the model. Glib specializes on std::string for Glib::Value to implement it as a cstring. Why, I don't know, it is essentially crippling std::string.
I am not sure that a std::string may contain a zero byte. Do you have the URL of something stating that this is possible so we can know for sure?
Certainly. www.cplusplus.com's std::string reference notes this: http://www.cplusplus.com/reference/string/string/assign/ "In the third member function version, the length is determined by parameter n, even including null characters in the content." Also, www.sgi.com's std::string reference notes this: http://www.sgi.com/tech/stl/basic_string.html const charT* c_str() const Returns a pointer to a null-terminated array of characters representing the string's contents. For any string s it is guaranteed that the first s.size() characters in the array pointed to by s.c_str() are equal to the character in s, and that s.c_str()[s.size()] is a null character. Note, however, that it not necessarily the first null character. Characters within a string are permitted to be null. As the ISO/IEC 14882 document is not free, I cannot cite it. However, the STL (although it was not called by that name at the time) was originally a work of HP and later SGI, whose documentation is cited above.
(In reply to comment #1) > Glib specializes > on std::string for Glib::Value to implement it as a cstring. Why, I don't > know, it is essentially crippling std::string. You are correct. Glib::Value<std::string> supports only a subset of the set of possible values of an std::string object. Which means that you cannot transparently store every possible std::string in a Glib::Value<std::string>. I agree that this is not ideal. However, there is a reason for this behavior: We want std::string to be recognizable as a string by the GObject type framework. Without the specialization, a custom boxed type would be created for it. While this would solve the problem you are having, it would make the value completely opaque to GObject. In practical terms, that means GtkTreeView will no longer know how to display it, GtkListStore and GtkTreeStore will no longer know how to sort it, and so on. The purpose of Glib::Value<> is to bridge the GObject and C++ type systems. In the GObject world, a string cannot contain a NUL byte. In the world of the C++ STL, it can. Conceptually, it is a string in either universe, and should therefore be exposed as such in the C++ API. It is unfortunate that these two ideas of what a string is don't match perfectly, but that is something we will have to live with. std::string::c_str() isn't applicable to all possible STL strings either, but it was still useful enough to have it in the standard. If you really really want this to be supported, you will have to push for a change to the C API. If you all you need is a work-around, encapsulate the std::string within a custom type. I'm closing this as WONTFIX, sorry.
As I'm not very familiar with the underlying glib C API, maybe you can help me understand a few things. As there is not inheritance in C, where does GString fall in relation to GObject and GValue (and also it to GObject)? I assume that the GString struct and the G_TYPE_STRING of GValue are not the same then (as GString seems to support the NUL character). I would say that the amount that I want this supported falls somewhere between really and really really. It's not enough to go and learn the C API and architecture of glib (at least not at the moment).
I don't think GString has anything to do with this. It's just an API that C coders use to make it easier to manipulate strings. Without looking into this much, I wonder if there is a way to provide our own callback to do the copying. Or maybe we can somehow allow a different Value specialization to be used, optionally. Reopening, because it should at least be documented.
Murray, this isn't limited to Glib::Value<std::string>. This restriction affects every single method which takes a string, stores it somewhere, and allows it to be retrieved again with a corresponding get method. It has always been like that, even before Glib::Value<> came into existence. And no, we can't provide our own callback without registering a new type. The whole point is to have std::string and Glib::ustring be treated as strings by GTK+ and other GObject APIs.
So, in summary, this is not something that we can do or want to do?
(In reply to comment #8) > So, in summary, this is not something that we can do or want to do? I'll assume so, in the absence of a reply.