On Thu, 14 Nov 2013 00:49:11 -0600, Paavo Helde
<
myfir...@osa.pri.ee> wrote:
>DSF <nota...@address.here> wrote in
>
news:g9g789tnbu5lfsjal...@4ax.com:
>
>> Hello,
>>
>> I have a class I've been writing where I frequently need to return
>> the class by value so that class members can be changed without
>> affecting the original. The problem is all of the copying.
>
>How do you know? Have you profiled the code and seen this is a
>bottleneck?
Making a copy of the original string object and altering the copy
takes on third the time of the return by value method. The majority
of my coding involves string manipulation, so it will make a
difference. (See my response to Jorgen Grahn for details.)
{snipped my example}
>>
>> I tried creating a "global" temp that could be used in place of each
>> function that used 'tfs'.
>
>Whatever you do, don't do this. Global (non-const) variables are evil,
>they complicate the data flow, make the optimizations harder and will
>create lots of troubles in recursive functions or if you ever want to go
>multithreaded.
In the first place *nothing* in C/C++ programming is evil! Arguably,
the worst that C/C++ has(had) to offer was gets(). And I wouldn't
call it evil. Poor design? Yes. Written that way because perhaps
back at that time an 80-column terminal also had a one line input
limit? Maybe. Could be interesting research if I have some time
sometime.
Note that "global" is in quotations. It wasn't literally a global,
it was merely a class member so it would persist when member function
Start (or any other return by value function) returned. It would be
"global" storage for return by value functions for that particular
instance of the class that would persist until another RBV function is
called.
One could think of class member variables as local to the class but
global to all its members. One thing I like about C++ is that one can
have variables common to a group of functions without having to pass
them around all of the time or make them global.
I had written a database in C, and while there was nothing wrong
with it. I disliked the fact that I had to pass a pointer to a
structure to almost every function. So I re-wrote it in C++ and the
structure's members became members of the class. Quite a few of the
functions no longer required any parameters.
As far as recursive code goes, C++ shines with it as well. My
database had two recursive functions. Each of them required some data
to be available at any depth. In C, those had to be globals. In C++,
they became members of the class, allowing more than one instance of
the database in a program. I maintained both versions for a while,
but found myself "forgetting" to update the C version more and more
often until I stopped.
A directory-traversing class for Windows I've written uses the same
method and takes it one step further. If it encounters a mount point,
It creates an instance of the class and runs it with the mount point
as the starting directory, making the whole class recursive.
{More snipping}
>>
>> So the question is: Are there any techniques for minimizing the
>> number of copies that occur when returning by value?
>
>This is mostly a job for the optimizer. In your example, the return value
>optimization (RVO) might be used by the compiler, eliminating one copy
>without any work from your part.
My compiler is several thousand years old and does very little
optimization. (There's a post of mine in clc on just how literal it
is at producing assembly code that's very close to the C code that
generated it.)
>Writing simple and straightforward code often helps the optimizer to do
>its work better.
See above.
>If the profiler still tells you that there is a bottleneck in copy, then
>you have to do something about it. One way would be to keep unchanging
>parts of the object in shared use, by adding some reference counters and
>so on, but this may also cause troubles in multithreading and is actually
>not guaranteed to speed things up.
The bottleneck is not in the actual copying of the string, but
rather the number of times return by value needs to copy said string.
>A better way to increase performance is to review your algorithms. The
>above method looks a lot like substr(), but still copies the whole string
>even if only a tiny portion is going to be used later. It should allocate
>space for and copy over only the needed amount of characters.
Hmmmm. I don't seem to have a substr() function in my RTL. Must've
been added in the last 500 years or so. :o) The string class that
comes with the compiler (not part of the STL) does have a SubString
that takes a starting position and length.
>BTW, what is the reason you are using your own string class and not
>something based on std::basic_string?
I learn best by doing. I can read about how to do something over
and over and not get it, yet experiment with it and get the concept in
one or two attempts. My main interest is string manipulation, whilst
books concentrate on mathematical examples. So what better way to
learn the ins and outs of an object than to create a string class?
I've put a lot of time and energy into the string class and I'm
currently working on wrapping the 40 or so C string functions I've
written into the string class. As I mentioned in another posting in
this thread, making a copy of the string and performing the alteration
on the copy is ~three times faster than return by value. So I'm
seriously considering dropping the return by value versions and using
only the in place ones.
Oh! And when I started working on the string class, I hadn't heard
of the STL.
>>
>> The ideal situation would be if the returned object could become the
>> unnamed object that's passed to operator=. But I think the compiler
>> would have to do that. And there's always the chance the function
>> could return an expression i.e. return tfs1 + tfs2.
>
>> My second question relates to the naming of member functions. Most
>> string-altering functions I've written have two versions, one that
>> returns by value and leaves the original alone, and one that alters
>> the string in place. For now, I'm using:
>>
>> FStringW FStringW::Start(size_t end);
>> void FStringW::StartIP(size_t end);
>
>The first one should be 'const', this also helps a bit to distinguish it.
It actually is. No typing. Strictly C&P from the header file:
FStringW Start(size_t end) const;
>For names, I have sometimes used a convention like Foo and ApplyFoo.
>
>But in your example, naming the functions something like Left() and
>Truncate() would be much better than Start() and StartIP() ;-) And even
>better would be to get rid of your class and use std::wstring or
>something else based on std::basic_string instead. Selecting good names
>is important for shortening the learning curve of any reader (including
>yourself in the future), with std::wstring the learning curve ought to be
>zero.
Not much chance of me getting rid of my class. I've put too much
time and energy into it. Not to mention that doing so would require a
rewrite of every program I've written that I'm actively maintaining.
Interestingly, Start was originally called Left, but I changed it
because it sounded too "BASICy". Speaking of selecting good names, I
find quite a few of the names in the STL counterintuitive. You "add"
an item to a vector with "push_back." Why not "add"?
And speaking of "vector," is the definition of the word as it's used
here unique to C++ or programming languages in general? Every
definition I can find relates to the position of one object from
another based on the length of a line drawn between the two objects
and the angle of that line from a baseline drawn through the first
object.