Making use of move semantics?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Making use of move semantics?

Daniel Boles
I still occasionally find myself reflexively std::move()ing strings into glibmm/gtkmm functions that I unconsciously see as taking ownership of their arguments - only to realise it makes no difference because all of them take strings as const&.

This made me wonder whether there are any cases where, if the user instructs so by using std::move(), glibmm/gtkmm functions could steal the string [ or at least it's c_str() ] and thus avoid having to copy it. All those copies quickly add up to a lot.

But my suspicion, without yet having dived into the code, is that most or all functions like this just take the c_str() and pass that to the underlying C methods, which would just see it as a char const* and copy it anyway - meaning we wouldn't be able to gain much or anything on the mm side.

Are my initial suspicions accurate, or are there any cases in which we can use move semantics to avoid copies, either on the mm or C sides?


Assuming nothing can be done here, if anyone can think of any other cases where we could make use of move semantics, that'd be a nice consolation prize. :D

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Making use of move semantics?

Murray Cumming-5
On Sun, 2017-05-21 at 10:36 +0100, Daniel Boles wrote:

> I still occasionally find myself reflexively std::move()ing strings
> into glibmm/gtkmm functions that I unconsciously see as taking
> ownership of their arguments - only to realise it makes no difference
> because all of them take strings as const&.
>
> This made me wonder whether there are any cases where, if the user
> instructs so by using std::move(), glibmm/gtkmm functions could steal
> the string [ or at least it's c_str() ] and thus avoid having to copy
> it. All those copies quickly add up to a lot.
>
> But my suspicion, without yet having dived into the code, is that
> most or all functions like this just take the c_str() and pass that
> to the underlying C methods, which would just see it as a char const*
> and copy it anyway - meaning we wouldn't be able to gain much or
> anything on the mm side.
>
> Are my initial suspicions accurate, or are there any cases in which
> we can use move semantics to avoid copies, either on the mm or C
> sides?

We've already used move/r-value-references in several places, but not
for string arguments.

I guess we might start taking std::string_view with C++17 instead of
std::string and this might largely take care of this.

> Assuming nothing can be done here, if anyone can think of any other
> cases where we could make use of move semantics, that'd be a nice
> consolation prize. :D


--
Murray Cumming
[hidden email]
www.murrayc.com

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Making use of move semantics?

Daniel Boles
On 21 May 2017 at 14:29, Murray Cumming <[hidden email]> wrote:

We've already used move/r-value-references in several places, but not
for string arguments.

I guess we might start taking std::string_view with C++17 instead of
std::string and this might largely take care of this.


I don't see how string_view would make any difference to whether or not we end up copying strings. Can it? 

Separately, how would string_view interact with Glib::ustring? Glancing at the available documentation, my guess is "it wouldn't", since it seems to be based on fixed-width characters, not to be context-sensitive. So it may not be a suitable replacement for ustring.


The more basic point from the latter question is that it's unbelievable we still don't have a real Unicode string type in C+ itself. I guess now that we're finally getting things like standardised filesystem and networking support, surely adequate Unicode handling can't be too far behind... I also note with interest the prospective graphics library, which was to be based on Cairo last time I looked.


Anyway, I think the ability to move strings or anything else in most cases will ultimately depend on the C libraries, and I think they rule it out. It's probably ultimately unimportant; I just tend to hate passing strings around in my own code and try to forward-then-move them all the way (leading to some private APIs that are probably horrifying to purists) so this idea came into my head and had to be typed out. :)


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Making use of move semantics?

Jonathan Wakely
In reply to this post by Daniel Boles
On 21 May 2017 at 10:36, Daniel Boles wrote:
I still occasionally find myself reflexively std::move()ing strings into glibmm/gtkmm functions that I unconsciously see as taking ownership of their arguments - only to realise it makes no difference because all of them take strings as const&.

This made me wonder whether there are any cases where, if the user instructs so by using std::move(), glibmm/gtkmm functions could steal the string [ or at least it's c_str() ] and thus avoid having to copy it. All those copies quickly add up to a lot.

You can't steal the contents of a std::string without access to its internals, which only the standard library has. You can't steal the c_str() ... I'm not even sure what that would mean.
 


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Making use of move semantics?

Daniel Boles
On 22 May 2017 at 11:23, Jonathan Wakely <[hidden email]> wrote:
On 21 May 2017 at 10:36, Daniel Boles wrote:
I still occasionally find myself reflexively std::move()ing strings into glibmm/gtkmm functions that I unconsciously see as taking ownership of their arguments - only to realise it makes no difference because all of them take strings as const&.

This made me wonder whether there are any cases where, if the user instructs so by using std::move(), glibmm/gtkmm functions could steal the string [ or at least it's c_str() ] and thus avoid having to copy it. All those copies quickly add up to a lot.
 
You can't steal the contents of a std::string without access to its internals, which only the standard library has. You can't steal the c_str() ... I'm not even sure what that would mean.

 

Saying "steal" was partly just my hamfisted way of explaining what the move constructor of an std::string (or Glib::ustring) would do. But yeah, partly I was half asleep, and imagining that maybe a C API could grab the char* from a std::string or whatever and leave it in a 'moved-from' kinda condition. Which is nonsense like you said, since there's no API for anything like that.

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

C++17's string_view (was Re: Making use of move semantics?)

Murray Cumming-5
In reply to this post by Daniel Boles
On Sun, 2017-05-21 at 15:03 +0100, Daniel Boles wrote:
[snip]
> > I guess we might start taking std::string_view with C++17 instead
> > of
> > std::string and this might largely take care of this.
>
>
> I don't see how string_view would make any difference to whether or
> not we end up copying strings. Can it?

I've tried it out now, so here are some thoughts:

I had hoped that std::string_view would avoid copying of C strings in
the temporary std::string in cases like this:

void use_string(const std::string& str) {
  gtk_label_set_text(str.c_str());
}
...
use_string("abc");

I think that the "abc" will be copied by the std::string constructor,
though we really only want a const char* anyway.

I had hoped that this would let us just use the original const char*:

void use_string_view(const std::string_view& str) {
  gtk_label_set_text(str.c_str());
}
...
use_string_view("abc");

But there is no std::string_view::c_str() because string_view doesn't
assume that its underlying array is null-terminated. It couldn't assume
that while sometimes being just a view of part an existing array.


So, we need to do this:

void use_string_view(const std::string_view& str) {
  gtk_label_set_text(str.to_string().c_str());
}
...
use_string_view("abc");

That std::string_view::to_string() does a copy, so I don't think we've
gained anything compared to taking a std::string.


And if we pass a std::string, we'd have a copy where we wouldn't have
had one before:

void use_string_view(const std::string_view& str) {
  gtk_label_set_text(str.to_string().c_str());
}
...
use_string_view(some_std_string);


So, I think:
1. We would use std::string_view everywhere if all the C functions took
  a length instead of assuming null-termination. That's not going to
happen.

2. Overriding all methods to take either a const char* or a std::string
(ignoring ustring for now), would avoid the extra array copy, but I
don't think it's worth it other than for methods that typically take
very large strings.

3. GTK+ functions that take very large strings tend to take a length,
to avoid the need for copying. For instance,
gtk_text_buffer_set_text(). We could take std::string_view there, but
then our use of std::string_view::to_string() would be wasteful when
someone passes a std::string to our method.

This is discouraging, so I hope I'm wrong about something.

> Separately, how would string_view interact with Glib::ustring?
> Glancing at the available documentation, my guess is "it wouldn't",
> since it seems to be based on fixed-width characters, not to be
> context-sensitive. So it may not be a suitable replacement for
> ustring.

I guess std::string_view deals with bytes, regardless of their
encoding, just as std::string does. If we keep using Glib::ustring,
we'd maybe want to have a Glib::ustring_view, at least as just a type
alias.

--
Murray Cumming
[hidden email]
www.murrayc.com

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Making use of move semantics?

Murray Cumming-5
In reply to this post by Daniel Boles
On Sun, 2017-05-21 at 15:03 +0100, Daniel Boles wrote:
> On 21 May 2017 at 14:29, Murray Cumming <[hidden email]> wrote:
> > We've already used move/r-value-references in several places, but
> > not
> > for string arguments.

I assume that std::string's own move operations give us enough
efficiency, so we wouldn't gave much by adding overloads such as
  set_something(std::string&& str);
but we could discuss specific code examples that we'd like to make more
efficient.

--
Murray Cumming
[hidden email]
www.murrayc.com

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++17's string_view (was Re: Making use of move semantics?)

Jonathan Wakely
In reply to this post by Murray Cumming-5
On 13 June 2017 at 08:31, Murray Cumming wrote:

So, I think:
1. We would use std::string_view everywhere if all the C functions took
  a length instead of assuming null-termination. That's not going to
happen.

2. Overriding all methods to take either a const char* or a std::string
(ignoring ustring for now), would avoid the extra array copy, but I
don't think it's worth it other than for methods that typically take
very large strings.

3. GTK+ functions that take very large strings tend to take a length,
to avoid the need for copying. For instance,
gtk_text_buffer_set_text(). We could take std::string_view there, but
then our use of std::string_view::to_string() would be wasteful when
someone passes a std::string to our method.

This is discouraging, so I hope I'm wrong about something.



Nope, you're correct. string_view is great if you stay in C++ world, but suboptimal when you need to pass the string to libc functions or C APIs taking null-terminated strings.

One possible approach (which I have no experience of in practice, only in theory) is to use string_view objects which explicitly include the null-terminator in their length:

template<typename C, typename T>
inline std::basic_string_view<C, T>
make_null_terminated_view(const C* s) noexcept
{ return { s, T::length() + 1 }; }

template<typename C, typename T>
inline bool
is_null_terminated_view(std::basic_string_view<C,T> sv) noexcept
{ return sv.length() && !sv.back(); }


And/or create your own cstring_view / zstring_view type which is guaranteed to be null-terminated:

struct cstring_view : std::string_view {
  cstring_view(const char* s)
  : std::string_view(s, traits_type::length(s)+1)
  { }
};

Unlike std::string which has a null-terminator after its content that isn't counted in the length, these string views would count the null character as part of their content. You'd need a little more care to use this (i.e. when using the length remember to subtract one where appropriate) but it does mean you can pass around views to null-terminated strings efficiently (along with their length, which is the advantage over a raw pointer).


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Loading...