Em quarta-feira, 28 de dezembro de 2016, às 00:50:40 BRST, Olaf van der Spek
escreveu:
> Hi,
>
> One frequently needs to append stuff to strings, but the standard way
> (s += "A" + "B" + to_string(42)) isn't optimal due to temporaries.
> A variadic append() for std::string seems like the obvious solution.
> It could support string_view, integers, maybe floats
> but without formatting options..
> It could even be extensible by calling append(s, t);
>
> append(s, "A", "B", 42);
>
> Would this be useful for the C++ std lib?
It can also be solved without code change, at least the string parts. The
following code in Qt does exactly one allocation:
Em quarta-feira, 28 de dezembro de 2016, às 10:09:30 BRST, Olaf van der Spek
escreveu:
> > > append(s, "A", "B", 42);
> > >
> > > Would this be useful for the C++ std lib?
> >
> > It can also be solved without code change, at least the string parts. The
>
> > following code in Qt does exactly one allocation:
> The non-string parts are kinda important.
I disagree. Anything but other strings and elements of strings (characters)
should be done with the proper string formatting functions. Otherwise, we'll
soon have someone asking for hex formatting, zero padding, etc. We already
have the right tools for that.
s << "A" << "B" << to_string(42);
Em quarta-feira, 28 de dezembro de 2016, às 22:54:32 BRST, Victor Dyachenko
escreveu:
> I think operator<<() would be better here:
>
> s << "A" << "B" << to_string(42);
>
> And about support for non-character types. It is useful at least for
> diagnostics. No need for powerful formatting here, just the ability to
> convert any fundamental type to text of any form. iostreams are to
> cumbersom, printf is not generic (one need to specify exact specifier for
> the type).
Why can't you use std::stringstream or another std::ostream here? I know
you're saying it's cumbersome, and I agree that the iostreams part of the
standard library is an extreme overkill (using polymorphism for things that
didn't need it). Still, we have the tool.
Why not fix iostreams instead?
result_t res = call(...);
if(failed(res)) throw std::logical_error(std::string() << "The call() returned " << res);
FIX: s/ states everything / states everywhere /
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/727df93a-3992-4c48-b729-cdc8815b5166%40isocpp.org.
std::string is already bloated too much.
Adding yet more bloat is hardly
the way to go.
I just don't think we should combine that with concatenation.
2016-12-29 17:14 GMT+01:00 Bengt Gustafsson <bengt.gu...@beamways.com>:
> Note that deferring number formatting to existing functions reduces the
> performance gain of this function: Each of the number formatter functions
> will have to allocate a std::string or similar and return it before the
> Append() function or similar gets hold of it. Thus we can never get down to
http://en.cppreference.com/w/cpp/utility/to_chars ;)
On 12/29/16 19:47, Nicol Bolas wrote:
> On Thursday, December 29, 2016 at 8:10:03 AM UTC-5, Andrey Semashev wrote:
>
> std::string is already bloated too much. Adding yet more bloat is
> hardly
> the way to go.
>
> I find this "too bloated" argument to be unconvincing.
>
> Does `std::basic_string` have functions that are, strictly speaking, not
> necessary? Yes. But that doesn't mean you shouldn't add function which
> actually /are necessary/ to the interface. The fact that a type already
> has unneeded member functions shouldn't stop you from putting needed
> ones in there.
I don't think formatting qualifies as the "necessary" or "needed"
functions for std::string.
> Now, you can argue that it actually /isn't/ necessary, that there are
> ways to achieve the same performance of a concatenation function without
> making it a member. But "appeal to bloat" isn't a valid argument against it.
It is, because that is exactly how std::string interface became bloated.
Consider the bunch of find*/rfind/compare member functions that are
currently present in std::string interface but could perfectly be
standalone. Some of them, in fact, already exist as standalone generic
algorithms, which a decent compiler will optimize to the same degree as
the dedicated member functions.
Em quinta-feira, 29 de dezembro de 2016, às 09:03:10 BRST, Nicol Bolas
escreveu:
> `to_chars` lacks the ability to tell you exactly how many characters a
> conversion will take. That's going to make it really difficult to
> pre-allocate the right amount of memory. At least, not without allocating a
> lot of extra space.
Which you can't know until you perform the conversion anyway. So any code that
tries to single-allocate a formatting chain needs to estimate with the worst
case scenario (which, for 'f', could be in the hundreds of characters) and
then trim the string.
As a QoI bonus, implementations can improve the estimation by performing log10
on the value, or log2 and divide by 3 (log2 is extremely fast).
Em sexta-feira, 30 de dezembro de 2016, às 14:52:21 BRST, Bo Persson escreveu:
> On 2016-12-30 13:09, Thiago Macieira wrote:
> > Em quinta-feira, 29 de dezembro de 2016, às 21:23:58 BRST, Nicol Bolas
> >
> > escreveu:
> >> It doesn't even make my executable bigger, since either way, it'll
> >> compile down to an inlined function.
> >
> > Uh... that makes it bigger, not smaller. Compiling to more inlined code
> > always makes it bigger. If you want to make it smaller, you have to call
> > the same out-of-line function.
>
> Not *always*. Inlined code can open up new opportunities for the
> optimizer, and save lots of code compared to passing parameters and
> calling out-of-line functions.
Right, I apologise for the generalisation.
But in this case it holds true: those functions aren't trivial, so inlining
them at every call place increases the code size, not descreases.
std::string generic(std::string look, std::string pattern)
{
auto loc = std::search(look
.rbegin(), look
.rend(),
pattern.rbegin(), pattern.rend());
if(loc != look
.rend())
{
loc += pattern.size();
return std::string(look
.begin(), loc.base());
}
else
return std::string{};
}
std::string member(std::string look, std::string pattern)
{
auto loc = look.rfind(pattern);
if(loc != std::string::npos)
{
return look.substr(0, loc);
}
return std::string{};
}
There is also a cost if you want to implement a class that mimics
std::string. I've done that a few times, and those member functions do
add complexity to the task.
There is obviously a cost for standard library implementers and standard
writers and committee.
Em sexta-feira, 30 de dezembro de 2016, às 17:56:00 BRST, Olaf van der Spek
escreveu:
> 2016-12-30 15:46 GMT+01:00 Thiago Macieira <thi...@macieira.org>:
> > Em sexta-feira, 30 de dezembro de 2016, às 16:58:18 BRST, Andrey Semashev
> > escreveu:
> > I explained in another email: convenience and difference in philosophy. I
> > do believe a class should provide as members most of the common
> > operations to be done to its data. That's why QString has startsWith()
> > and endsWith(), which are trivially easy to implement.
>
> However, if those are implemented as free functions taking something
> like string_view, other string types would be able to take advantage
> of them too...
I don't see it that way. Like I said, they're trivially easy to implement, so
they're trivially easy to reimplement. They can be implemented multiple times,
one for each string-like class.
Interface conciseness is less important than overall readability. Consider a relatively simple programming task. You're given two strings. You want to find the last instance of string B within string A, then generate a string that consists of all characters before the instance you found.
This is what the implementation based on generic algorithms and iterators looks like:
std::string generic(std::string look, std::string pattern)
{
auto loc = std::search(look
.rbegin(),look
.rend(),
pattern.rbegin(), pattern.rend());
if(loc !=look
.rend())
{
loc += pattern.size();
return std::string(look
.begin(), loc.base());
}
else
return std::string{};
}
Understanding this code requires being well versed in how reverse iterators work. Two particularly non-obvious things I had to do were: 1) reversing the pattern as well as the string being searched and 2) offsetting `loc` by the pattern's size, since that's not the actual location.
This is what the index&member function version looks like:
std::string member(std::string look, std::string pattern)
{
auto loc = look.rfind(pattern);
if(loc != std::string::npos)
{
return look.substr(0, loc);
}
return std::string{};
}
That's much shorter and more easily understood. There's no need to reverse the pattern's range, nor the mysterious offset. The only thing that might be at all confusing is the test against `npos`. But that's ultimately no different from testing against `look.rend()`.
std::string generic(std::string look, std::string pattern)
{
auto loc = std::find_end(look
.begin(), look
.end(),
pattern.begin(), pattern.end());
if(loc != look
.end())
{
return std::string(look
.begin(), loc);
}
return std::string{};
}
std::string generic(std::string look, std::string pattern)
{
auto loc = std::find_end(look, pattern);
if(loc != look.end())
{
return std::string(look.begin(), loc);
}
return std::string{};
}
On Friday, December 30, 2016 at 1:16:27 PM UTC-5, Nicol Bolas wrote:Interface conciseness is less important than overall readability. Consider a relatively simple programming task. You're given two strings. You want to find the last instance of string B within string A, then generate a string that consists of all characters before the instance you found.
This is what the implementation based on generic algorithms and iterators looks like:
std::string generic(std::string look, std::string pattern)
{
auto loc = std::search(look
.rbegin(),look
.rend(),
pattern.rbegin(), pattern.rend());
if(loc !=look
.rend())
{
loc += pattern.size();
return std::string(look
.begin(), loc.base());
}
else
return std::string{};
}
Understanding this code requires being well versed in how reverse iterators work. Two particularly non-obvious things I had to do were: 1) reversing the pattern as well as the string being searched and 2) offsetting `loc` by the pattern's size, since that's not the actual location.
This is what the index&member function version looks like:
std::string member(std::string look, std::string pattern)
{
auto loc = look.rfind(pattern);
if(loc != std::string::npos)
{
return look.substr(0, loc);
}
return std::string{};
}
That's much shorter and more easily understood. There's no need to reverse the pattern's range, nor the mysterious offset. The only thing that might be at all confusing is the test against `npos`. But that's ultimately no different from testing against `look.rend()`.Seems to me that's only because you chose a sub-optimal algorithm. You want the one that matches string::rfind, but you used the one that matches string::find instead, so you had to add extra code to make it work right. Try this:
But hey, what proposal are we discussing, again? :) After all this
rather philosophical discussion,
I'm not quite sure what we're looking at. ;)
Hello,It would be useful, but will however probably cause temporary string in variadic iteration (I'm not a variadic template expert).but my dream is more to have a library like this one standardized https://github.com/fmtlib/fmtLaurent
Le mercredi 28 décembre 2016 09:50:40 UTC+1, Olaf van der Spek a écrit :Hi,
One frequently needs to append stuff to strings, but the standard way
(s += "A" + "B" + to_string(42)) isn't optimal due to temporaries.
A variadic append() for std::string seems like the obvious solution.
It could support string_view, integers, maybe floats
but without formatting options..
It could even be extensible by calling append(s, t);
append(s, "A", "B", 42);
Would this be useful for the C++ std lib?
I agree that it would be useful to have a formatting function that appends to existing string. Incidentally, the facility to append to strings and containers with contiguous storage was recently contributed to fmt: https://github.com/fmtlib/fmt/pull/450
>
> I wasn't saying you can't use a string, I was trying to say that it seems
> to be fmt should be able to format into more than just a string.
> Like an std::array, or std::vector or or whatever, via an adaptor or
> otherwise. anything conforming to a buffer concept of some kind.
Why can't we call that adaptor "std::string"?
> i.e. any thing that can present a block of memory containing characters.
And that can be reallocated to extend its size, or truncated once the true
size is known. And is contiguous, of course.
> quite how that works I'm not sure yet, I specifically wouldn't be keen to
> only a format that worked only with a string and then need to copy that
> somewhere else unless there was a good reason. There may be a good reason,
> but it seems we should be aware of this.
Let's start with the good reason. It has to be good enough to overcome the
need to make the library function more complex for the 99.99% of the uses.
I know he C library does it, and that it can reuse the same formatters to
output to a preallocated string (snprintf) or a file (fprintf). But that
probably only works because the C library internally writes to a buffer and
flushes it periodically. So in order to do the same for us, for multiple
different output classes, the formatting function would likely have to have
its own buffering. That's what I meant when I said it would be more complex
than it needs to be for the 99.99% of the uses.
> I also don't want to format have to create that thing either, so I don't
> see why we should be forced to dynamically create memory in order to format
> which a string only thing would force.
If it can't allocate or reallocate, then it must be prepared for failing in
case of buffer overrun. When formatting, you usually don't want that because
formatting can't be easily restarted from where it failed.
Why can't we call that adaptor "std::string"?
Let's start with the good reason. It has to be good enough to overcome the
need to make the library function more complex for the 99.99% of the uses.
- 2nd Be able to create a string from a predefined format
This is what printf is often used for. Having something similar in cpp would be nice. But I think this will require adding something on the std::string interface since I hardly see an algorithm being able to do that because it is something quite string-specific .
Just my 2 cents,
Masse Nicolas.
I note we have std::size() and std::data(). A question for the Committee, if we also had std::is_fixed_size() and std::resize() could we not make an std::format(container, ...) work with any of these types without anything concepty being required?Formatting into a container is overly restrictive; one may also want to be able to format into a preallocated subrange (e.g. character positions 10-20 of a string) or to an output stream (console, file or network).I think the more general concept is OutputRange, which could encapsulate e.g. a back inserter for string or vector (expandable storage), a pair of char pointers for an array, C array or subrange, or an ostreambuf_iterator for an output stream.
Formatting into a container is overly restrictive; one may also want to be able to format into a preallocated subrange (e.g. character positions 10-20 of a string) or to an output stream (console, file or network).I think the more general concept is OutputRange, which could encapsulate e.g. a back inserter for string or vector (expandable storage), a pair of char pointers for an array, C array or subrange, or an ostreambuf_iterator for an output stream.
Hi,
One frequently needs to append stuff to strings, but the standard way
(s += "A" + "B" + to_string(42)) isn't optimal due to temporaries.
A variadic append() for std::string seems like the obvious solution.
It could support string_view, integers, maybe floats
but without formatting options..
It could even be extensible by calling append(s, t);
append(s, "A", "B", 42);
Would this be useful for the C++ std lib?
On sábado, 14 de janeiro de 2017 14:33:01 PST gmis...@gmail.com wrote:
> I was just in the process of replying to say that decoupling formatting
> from string would be great. And that seems to do that.
> I didn't see exactly how it's used but it seems (but I'm sure someone will
> correct me here) it would be nice if this worked:
>
> std::vector v;
> So fmt(v. "whatever");
Sorry, but.. why?
Why can't you just use std::string? Why does it need to be something
different?
The formatting code is likely to be big, so it's most likely not going to be
inline. Therefore, it can't be templated.