I want to put this logic into a helper function:
std::stringstream ss;
std::string str = "some str";
if (do_check(str)) {
ss << str;
} else {
ss << do_edit(str);
}
and instead write it like
std::string edit_str(const std::string &str) {
if (do_check(str)) {
return str;
}
return do_edit(str);
}
main() {
std::stringstream ss;
std::string str = "some str";
ss << edit_str(str);
}
However, this creates a copy when I return str. Is there any way to write this as a helper function with no/minimal overhead? I'm open to changing the parameter/return type, using output parameters, templates, etc., but I would like to avoid using macros.
CodePudding user response:
The standard library sets the standard for how to do this with functions like std::quoted
. The function actually does no work; it just stores references to the arguments into an object, called the IO manipulator. Then the operator<<
for that object is what does the actual work. We can simply follow their example.
struct edited { // playing a little fast-and-loose; instead of a dedicated function we just aggregate-initialize this struct with a function-like syntax (requires C 20)
std::string const &str;
friend std::ostream &operator<<(std::ostream &out, edited const &thiz) {
// you can put whatever block of code you'd like to factor out in here
if (do_check(thiz.str)) out << thiz.str;
else out << do_edit(thiz.str);
return out;
}
};
int main() {
std::string str = "some string";
std::cout << edited(str);
}
You do have to be careful not to accidentally use edited(str)
like a string. The only thing you should be doing with it is <<
ing it into a stream. If you need a std::string
from it at some point, make a std::stringstream
and use that to collect the output. Also, it may be beneficial to inline do_edit
into operator<<
and adjust it so it does not return an intermediate string that then gets output but can just output directly, too.
CodePudding user response:
Sometimes it's ok to just do things a bit more openly:
ss << (is_whavever(str) ? str : fix_whatever(str));
Where your do_check
and do_edit
functions are just named descriptively.
Once you start having this piece of code in too many places, you can start thinking if you should write a helper class (see other answers). Also, if you are making a library with API you are not allowed to change later, then you might start with a bit of extra features. But otherwise, don't over-engineer it.
CodePudding user response:
You can use a custom type that has an overload for <<
:
#include <string>
#include <sstream>
#include <iostream>
bool do_check(const std::string&) {return false;}
void do_edit(std::string& in){
in = "foo";
}
struct checked_string {
std::string& value;
};
std::ostream& operator<<(std::ostream& out,const checked_string& cs) {
if (! do_check(cs.value)) {
do_edit(cs.value);
}
out << cs.value;
return out;
}
int main() {
std::string str = "some str";
std::cout << checked_string{str};
}