char* string_view::to_c_str(array_view<char> buf) {
auto l = std::min(length(), buf.size()-1);
std::memcpy(buf.data(), data(), l);
buf[l] = '\0';
return buf.data();
}
string_view is not compatible with legacy C API's because its not null terminated. The solution is to make a copy either into a std::string or a fixed size buffer and then call the C function with that.
It would be nice to have a to_c_str() helper method which would handle the copying and null terminating correctly. This makes adding C compatibility a bit easier when we don't want to pay for a memory allocation with a std::string.
On Tuesday, December 9, 2014 2:26:48 PM UTC-8, Matthew Fioravante wrote:string_view is not compatible with legacy C API's because its not null terminated. The solution is to make a copy either into a std::string or a fixed size buffer and then call the C function with that.That is usually a bummer. I've filed bugs against C libraries that don't have an explicitly-sized string interface. Naked calls to system APIs (POSIX, Win32, etc.) are rare enough in my experience to not be worth over-engineering solutions for them.
It would be nice to have a to_c_str() helper method which would handle the copying and null terminating correctly. This makes adding C compatibility a bit easier when we don't want to pay for a memory allocation with a std::string.My concern is that this returns an owning raw pointer. In a time when we're trying to tell everyone to stop doing that and to use unique_ptr or whatnot instead.
All that's required is that the user construct an array_view to represent the memory region to be written to.
void call_c_function(const std::string_view& sv) {
std::string s = sv.to_string();
c_function(s.c_str());
}
c_function(sv.to_string().c_str());On Wednesday, December 10, 2014 12:40:54 PM UTC+11, Matthew Fioravante wrote:All that's required is that the user construct an array_view to represent the memory region to be written to.
If the user has to do some work anyway, they might as well just create a string from the string_view, e.g.:
float strtol(string_view s, string_view& tail) {
char buf[256]; //Can make this as small as whatever is the maximum string representation for floating point + 1
s.to_c_str(buf);
char* e = buf;
auto f = strtof(buf, &e);
tail = s;
tail.remove_prefix(e - buf);
return f;
}
Here is another example, where you can save an allocation if your string is "small"
//From C API
int c_function(const char* s);
//string_view wrapper
int c_function(string_view s) {
std::string str;
char buf[4096];
char* p;
if(s.length() < sizeof(buf)-1) {
p = s.to_c_str(buf);
} else {
str = s;
p = str.c_str();
}
return c_function(p);
}
On Tuesday, December 9, 2014 11:12:20 PM UTC-5, sque...@gmail.com wrote:On Wednesday, December 10, 2014 12:40:54 PM UTC+11, Matthew Fioravante wrote:All that's required is that the user construct an array_view to represent the memory region to be written to.
If the user has to do some work anyway, they might as well just create a string from the string_view, e.g.:
I'm not sure people are actually understanding what I am proposing. Creating a std::string requires a memory allocation which may be unacceptable for performance, particularly when the string has a known upper bound on its length.
I'm not sure people are actually understanding what I am proposing. Creating a std::string requires a memory allocation which may be unacceptable for performance, particularly when the string has a known upper bound on its length.
On Tuesday, December 9, 2014 11:12:20 PM UTC-5, sque...@gmail.com wrote:On Wednesday, December 10, 2014 12:40:54 PM UTC+11, Matthew Fioravante wrote:All that's required is that the user construct an array_view to represent the memory region to be written to.
If the user has to do some work anyway, they might as well just create a string from the string_view, e.g.:
I'm not sure people are actually understanding what I am proposing. Creating a std::string requires a memory allocation which may be unacceptable for performance, particularly when the string has a known upper bound on its length.
On 10 December 2014 at 09:27, Matthew Fioravante <fmatth...@gmail.com> wrote:I'm not sure people are actually understanding what I am proposing. Creating a std::string requires a memory allocation which may be unacceptable for performance, particularly when the string has a known upper bound on its length.
That isn't sufficient. You also have to know that you can write a '\0' into a byte both legally (i.e., the string_view has modifiable space underneath it)
and semantically (doing so won't, say, incorrectly truncate the string for the purposes of calling a C function on it).
This micro optimization is fragile and error-prone.
We aren't writing to the bytes pointed to by the string_view. We're copying the data from the string_view to another buffer, null terminating it, and passing it to the c function. We cannot write to the data pointed to by the view because first the view is const and second we cannot write a 0 to the byte after the view. Take a look at the examples again.
On 10 December 2014 at 19:04, Matthew Fioravante <fmatth...@gmail.com> wrote:We aren't writing to the bytes pointed to by the string_view. We're copying the data from the string_view to another buffer, null terminating it, and passing it to the c function. We cannot write to the data pointed to by the view because first the view is const and second we cannot write a 0 to the byte after the view. Take a look at the examples again.Your implementation has something writing into the data referred to in an array_view, which isn't allowed last I checked.
Allowing modification of contents through an array_view is a bad idea for exactly the same reasons it is a bad idea to allow it in string_view.
The next problem with it is the std::min call, which makes it very difficult to reason about whether or not the resulting string is truncated. This alone makes it almost as bad to reason about as using strncpy.
It also doesn't handle zero-length destination buffers, which is problematic if you want to fill up runtime sized buffers.
Generally, we prefer to return things and take advantage of RVO rather than pass them in and replace their contents. This could be accomplished with, say, a templated size and return a std::array of that size (or throw if it isn't large enough),
but I just don't find this kind of micro optimization necessary often enough to be worthwhile as a standard feature.
On Wednesday, December 10, 2014 9:38:48 PM UTC-5, Nevin ":-)" Liber wrote:On 10 December 2014 at 19:04, Matthew Fioravante <fmatth...@gmail.com> wrote:We aren't writing to the bytes pointed to by the string_view. We're copying the data from the string_view to another buffer, null terminating it, and passing it to the c function. We cannot write to the data pointed to by the view because first the view is const and second we cannot write a 0 to the byte after the view. Take a look at the examples again.Your implementation has something writing into the data referred to in an array_view, which isn't allowed last I checked.array_view is not a const view like string_view and should not be.
Truncation is part of contract of the function. If you don't want truncation check the length first or use std::string.
Here is one use case we have today where allocating memory for a std::string is totally unnecessary and would have a large impact on performance.
--
---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/LEPhIf_msXw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.