const auto& for storing functions results, is it worthwhile? - c++

Let assume that we have a function that returns a complex object like std::string:
std::string find_path(const std::string& filename);
Is it worthwhile to store the result of calling that method in the const auto&?
void do_sth() {
//...
const auto& path = find_path(filename);
//...
}
That kind of approach prevents copying/moving the object. So it is good. But on the other hand, auto has been introduced to unify the left side of assignation. Herb Sutter in his presentation from CppCon2014 mentions about C++ left-to-right modern style https://www.youtube.com/watch?v=xnqTKD8uD64 (39:00-45:00).
In C++98 storing the std::string at const ref was fine. How is it in C++11?
Update (2016-07-27 2:10 GMT+0):
Sorry, my question was not precise. I meant the coding style - is it better to add const & or just stay with auto only and let the compiler do whatever it want.
Updated example:
unsigned int getTimout() { /* ... */ }
int getDepth() { /* ... */ }
std::string find_path(const std::string& filename,
unsigned int timeout,
int depth) { /* ... */ }
void open(const std::string& path) { /* ... */ }
Two approaches:
void do_sth() {
//...
auto timeout = getTimeout();
auto depth = getDepth();
const auto& path = find_path(filename, timeout, depth);
open(path)
//...
}
vs
void do_sth() {
//...
auto timeout = getTimeout();
auto depth = getDepth();
auto path = find_path(filename, timeout, depth);
open(path);
//...
}
The question: should we
use const auto& to store complex return objects and auto for primitives, or
use auto for everything to keep the left-to-right modern C++ style that Herb mentioned in his presentation (link above).

In C++98 storing the std::string at const ref was fine. How is it in C++11?
Binding a const reference to a temporary string object is fine in C++11. It still has the same behaviour as before.
The theoretical cost of copy-initialization from a temporary (avoiding of which is the advantage of using a const reference) has been greatly reduced in C++11, since moving a string is far cheaper than copying.
The practical cost of copy-initialization has not changed with optimizing compilers, because they may elide the copy/move anyway, so there is no cost - although, whether that is possible, depends on how find_path is implemented.
If you absolutely want to avoid copying/moving the returned string and cannot assume copy elision, then you must create the object outside the function, and pass it to the function by reference. Binding a reference to the return value is not sufficient.
If you want to avoid copying/moving the returned string and can assume copy elision, then using a regular object is just as good as a const reference.

Related

What should a void*/std::size_t function argument pair be changed to for an interface that writes binary files?

The C++ core guidelines state many times that using void* as an argument is at best confusing and at worst error-prone. My favorite mention is at the end:
C++ Core Guidelines: To-do: Unclassified proto-rules
Anyone writing a public interface which takes or returns void* should have their toes set on fire.
That one has been a personal favorite of mine for a number of years. :)
That said:
What should this function signature be changed to in order to comply with this suggestion? Currently it works with anything, that can be reinterpreted as a const char*:
bool writeBufferToFile(void* buffer, std::size_t size, const std::string& filePath) const
{
namespace FS = std::filesystem;
FS::path p(filePath);
p.make_preferred();
bool not_valid_path = FS::is_directory(p);
bool invalid = not_valid_path;
if(invalid) { return false; }
std::ofstream ofs;
ofs.open(p.string(), std::ios_base::binary);
if(ofs)
{
ofs.write(reinterpret_cast<const char*>(buffer), size);
return true;
}
return false;
}
What you want is not two parameters, a pointer and a size, but one parameter that represents a binary chunk of data. In an ideal world, you might use something like std::vector<std::uint8_t> const&, but the problem with this is that it forces callers to do an allocate/copy/free if they happen to store the data some other way.
So what you really want is a class that represents a binary chunk of data but doesn't own that data and can be constructed, copied, and destroyed very cheaply regardless of how the underlying data is stored. This avoids the need to have two parameters to express one concept.
A class that encapsulates this is often called a Slice. So I would suggest:
class Slice
{
private:
std::uint8_t const* data_;
std::size_t size_;
...
};
bool writeBufferToFile (const Slice& data, const std::string& filePath) const
Your Slice class can easily be constructed from a std::vector <std::uint8_t> or pretty much any other sensible way of holding a range of bytes.
I went with std::any. It can be used as a type-safe replacement for void*.
Motivating articles:
std::any: How, when, and why
std::any - comparison with void* and motivating examples
bool WriteBufferToFile(const std::any& buffer, std::size_t size, std::filesystem::path filepath) noexcept {
namespace FS = std::filesystem;
filepath = FS::absolute(filepath);
filepath.make_preferred();
const auto not_valid_path = FS::is_directory(filepath);
const auto invalid = not_valid_path;
if(invalid) {
return false;
}
if(std::ofstream ofs{filepath, std::ios_base::binary}; ofs.write(reinterpret_cast<const char*>(&buffer), size)) {
return true;
}
return false;
}

Will (N)RVO be applied with my function in this situation?

I have the following code: (ok, in reality it's much more complicated, but I simplified it to make it easier to understand. so please disregard the things that seems stupid. I can't change them in my real situation)
#include <string>
using std::string;
ReportManager g_report_generator;
struct ReportManager
{
// I know, using c_str in this case is stupid.
// but just assume that it has to be this way
string GenerateReport() { string report("test"); return report.c_str(); }
}
string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}
void main()
{
string s = DoIt(true);
}
Will (N)RVO be applied with my functions?
I did a bit of research, and it would seem like it, but I'm not really convinced and I'd like a second opinion (or more).
I'm using Visual Studio 2017.
To solve your problem I rewrote it.
#include <string>
struct string : std::string {
using std::string::string;
string(string&& s) {
exit(-1);
}
string(string const&) {
exit(-2);
}
string() {}
};
struct ReportManager
{
// I know, using c_str in this case is stupid.
// but just assume that it has to be this way
string GenerateReport()
{
string report("test");
return report.c_str();
}
bool isEmpty() const { return true; }
void clear() const {}
};
ReportManager g_report_generator;
string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}
int main()
{
string s = DoIt(true);
}
The trick with this rewriting is that elision permits skipping copy/move ctors. So every time we actually copy an object (even if inlined), we'll insert an exit clause; only by elision can we avoid it.
GenerateReport has no (N)RVO or any kind of elision, other than possibly under as-if. I doubt a compiler will be able to prove that, especially if the string is non-static and large enough to require heap storage.
For DoIt both NRVO and RVO is possible. Elision is legal there, even with side effects.
MSVC fails -- notice calls to
??0string##QAE#$QAU0##Z, which is the move constructor of my local string class.
When I force the possible RVO case to run by saying it is empty, you'll see that the compiler also fails to RVO optimize here; there is an exit(-1) inlined into the disassembly.
Clang manages to RVO the return string(); but not NRVO the return val;.
By far the easiest fix is:
string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
return [&]{
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}();
}
which has double RVO, and a lambda that does simple NRVO. Zero structural changes to your code, and functions which C++98 compilers can elide return values on (well, they don't support lambda, but you get the idea).
I do not think (N)RVO is possible in either functions. GenerateReport has to construct a string from character array, there is nothing left for NRVO. DoIt returns two different values through it control path, which makes it impossible to perform NRVO as well.

Is there empty std::string defined somewhere?

Is there empty std::string defined somewhere?
What I mean is following:
I can do:
// code #1
int process(const char *s = nullptr);
// later I can use:
process();
However, if I do:
// code #2
int process(const std::string &s = "");
// later I can use:
process(); // wait, an object is created...
It compiles and works, when I use the function, there is unneeded object creation.
Is there standard way to do following:
// code #3
const std::string EMPTY_STR;
int process(const std::string &s = EMPTY_STR);
// later I can use:
process(); // fine, no object is created...
This is not bottleneck, nor it is a premature optimization.
I believe C++ is not Java, so right way is not to create objects that you do not need.
Also I believe code 3 looks much better than code 2 and also it show the intention that string is empty (and probably will not be used), than code 2, where is not very clear why the string is "".
There's no (safe) way of making a reference refer to nothing. If you don't want an object to be created at all, you have to use a pointer;
int process(const std::string *s = nullptr);
If you want to use references, there must be an object somewhere. For that purpose, you could use a default-constructed (empty) string as default argument;
int process(const std::string& s = std::string());
First of all, I am slightly surprised you need an OBJECT. It is going to be a const object anyway, so what does it give you which simple const char* does not?
Second, there is a solution to your problem in a generic way. There is a genuine problem with following omnipresent code:
void foo(const std::string& data);
It allows using foo with both std::strings and const char*, and suits almost everybody. It does not matter for majority of users of foo(), but for someone really fixed on performance (as in nanoseconds) creation of temporary std::string from their const char* is a sore point. To solve this problem, StringRef was invented. It is not available in std, but it exits in many libraries including boost, and is simple to implement. The idea is following:
class StringRef {
...
StringRef(const std::string& str) : b(str.begin()), end(str.end()) {}
StringRef(const char* str) : b(str), end(str + strlen(str)) {}
...
private:
const char* const b;
const char* const e;
};
void foo(const StringRef str);

C++: Immutable method versions

In C++11, what is the best way to provide two versions of a method, one to modify the object itself and one to return a modified copy?
For example, consider a string class which has the "append(string)" method. Sometimes you might want to use append to modify your existing string object, sometimes you might want to keep your string object the same and create a copy.
Of course, I could just implement the first version and manually create a new object everytime I need one but that adds multiple temporary variables and lines of code to my project.
If it is still not clear what I am trying to do:
String s1("xy");
String s2 = s1.appendCopy("z");
s1.appendThis("w");
// s1 == "xyw"
// s2 == "xyz"
In Ruby there is a concept (or rather, a naming convention) which says for such methods, there are two variants: append (creates a new String) and append! (modifies this object)
C++ does not have something like this, so I would be stuck with ugly method names like "appendCopy".
Is there a good way to implement what I am trying to do?
So far, the best idea I had would be to make the modifying versions class members and the copying/immutable versions static methods which take the object to work on as a const argument.
There is actually a guideline, expressed by Herb Sutter in GotW #84:
Prefer non-member non-friend functions.
In your specific case, append (in-place) requires modifying the existing string so is well-suited to be a class-method, while append (copying) does not, so (following the guideline) should not be a class-method.
Thus:
void std::string::append(std::string const&);
inline std::string append(std::string left, std::string const& right) {
left.append(right);
return left;
}
After popular request, here are two overloads that can be used to optimize performance. First the member-version that may reuse its argument's buffer:
void std::string::append(std::string&& other) {
size_t const result_size = this->size() + other.size();
if (this->capacity() < result_size) {
if (other.capacity() >= result_size) {
swap(*this, other);
this->prepend(other);
return;
}
// grow buffer
}
// append
}
And second the free-function that may reuse its right-hand buffer:
inline std::string append(std::string const& left, std::string&& right) {
right.prepend(left);
return right;
}
Note: I am not exactly sure there are not ambiguous overloads manifesting. I believe there should not be...
With the new move semantics you can write:
class A{
public:
// this will get the property
const dataType& PropertyName() const { return m_Property; }
// this wil set the property
dataType& PropertyName() { return m_Propery; }
private:
dataType m_Propery;
};
main()
{
A a;
a.PropertyName() = someValueOfType_dataType; // set
someOtherValueOfType_dataType = a.PropertyName(); // get
}

What should the return type be when the function might not have a value to return?

In the old days, you might have a function like this:
const char* find_response(const char* const id) const;
If the item could not be found, then a null could be returned to indicate the fact, otherwise obviously return the relevant string.
But when the function is changed to:
const std::string& find_response(const std::string& id) const;
What do you return to indicate item not found?
Or should signature really be:
bool find_response(const std::string& id, std::string& value) const;
What would be the most elegant modern C++ way?
boost::optional. It was specifically designed for this kind of situation.
Note, it will be included in upcoming C++14 standard as std::optional. Update: After reviewing national body comments to N3690, std::optional was voted out from C++14 working paper into a separate Technical Specification. It is not a part of the draft C++14 as of n3797.
Compared to std::unique_ptr, it avoids dynamic memory allocation, and expresses more clearly its purpose. std::unique_ptr is better for polymorphism (e.g. factory methods) and storing values in containers, however.
Usage example:
#include <string>
#include <boost/none.hpp>
#include <boost/optional.hpp>
class A
{
private:
std::string value;
public:
A(std::string s) : value(s) {}
boost::optional<std::string> find_response(const std::string& id) const
{
if(id == value)
return std::string("Found it!");
else
return boost::none;
//or
//return boost::make_optional(id == value, std::string("Found it!"));
}
//You can use boost::optional with references,
//but I'm unfamiliar with possible applications of this.
boost::optional<const std::string&> get_id() const
{
return value;
}
};
#include <iostream>
int main()
{
A a("42");
boost::optional<std::string> response = a.find_response("42"); //auto is handy
if(response)
{
std::cout << *response;
}
}
What would be the most elegant modern C++ way?
There's, as always, not just one solution to this problem.
If you decide to go for any solution that references the original resonse instance, you're on a slippery road when it comes to aliasing and memory management, especially in a multi threaded environment. By copying the response to the caller, no such issues arises.
Today, I would do this:
std::unique_ptr<std::string> find_response(const std::string& id) const;
That way, you can check for nullptr as "in the olden days" and it's 100% clear who's responsibility it is to clear up the returned instance: the caller.
The only downside I see of this, is the additional copy of the response string, but don't dismiss that as a downside until measured and proven so.
Another way is to do as is done when searching std::set<> and std::map<> - return a std::pair<bool, const char*> where one value is bool is_found and the other is const char* response. That way you don't get the "overhead" of the additional response copy, only of the returned std::pair<> which is likely to be maximally optimized by the compiler.
If the function is returning a string by reference, but needs the ability to indicate that no such string exists, the most obvious solution is to return a pointer, which is basically a reference that can be null, i.e. exactly what was sought after.
const std::string* find_response(const std::string& id) const;
There are several good solutions here already. But for the sake of completeness I'd like to add this one. If you don't want to rely on boost::optional you may easily implement your own class like
class SearchResult
{
SearchResult(std::string stringFound, bool isValid = true)
: m_stringFound(stringFound),
m_isResultValid(isValid)
{ }
const std::string &getString() const { return m_stringFound; }
bool isValid() const { return m_isResultValid; }
private:
std::string m_stringFound;
bool m_isResultValid;
};
Obviously your method signature looks like this then
const SearchResult& find_response(const std::string& id) const;
But basically that's the same as the boost solution.
Use of pointers in C++ is forgiven if you need to return a nullable entity. This is widely accepted.
But of course bool find_response(const std::string& id, std::string& value) const; is quite verbose. So it is a matter of your choice.
I think the second way is better. Or you can write like this:
int find_response(const std::string& id, std::string& value) const;
if this function return -1, it tells that you don't find the response.