Home > Back-end >  How to return a string with no overhead inside helper function
How to return a string with no overhead inside helper function

Time:12-30

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};
}
  •  Tags:  
  • c
  • Related