I want to create a universal template function that replaces all occurrences of a search string in a given string object with the replace text lazy-evaluated, possibly by calling a functor.
The main differences from the function I need, to the usual functions given in answers to questions like How to replace all occurrences of a character in string? are:
- The string to be manipulated, as well as the search string, needs not to be of a concrete type like
std::string. It's sufficient to be string-like. - The new text parameter, used to replace the search string, may be a value or a functor that will be invoked to deliver a value
- The new text is only evaluated, when the search string is found
- The new text is only evaluated once, even when it is used multiple times (in case it occurs more than once in the string to be manipulated)
- The new text can have a type different from the type of the search string or the string to be manipulated. It is only required to be convertible into a
std::string_view<>, compatible to the string to be manipulated. This means thatchar*,std::string_view,std::string,char*(*)()andstd::function<string()>are all fine.
Here is what I want to write, that makes this design very universal and very different from the answers in similar but simpler questions:
The function should accept all possible kinds of text types (
char*,std::string_view,std::string,char*(*)(),std::function<string()>, ...) for the new (replace) text. Anything that evaluates to something that is assignable to astd::basic_string_view<>should also work as an argument for the new replacement text. Especially text coming from a lazy-evaluated functor.The function should be able to manipulate text in all
std::basic_string-like string classes, especiallystd::stringandstd::wstring. And usable also on other classes that provide the required functions in their interface.The function must handle the case, where the new text comes from a call to a function returning a temporary object (
std::stringfor example). So it needs to extend the lifetime of this temporary (or copy the value, but this seems inefficient and should be avoided)The new (replace) string parameter should only be evaluated once, even when several replacements occur (at least in case the replace parameter is a functor).
The function should be optimized also for the case, where no replacement occurs. That is, it should not create expensive temporary objects (such as a temporary
std::string), if they are not subsequently used. (Reason: My use case consists of a very large number of calls to the wanted replace function, with only a few actual replacements.)I want an all-in-one single function.
I want the code to work with VS2022 and C++20, but solutions for other compilers and newer C++ revisions may help others (so I do not add the VS2022 tag)
The function could have a prototype like this:
auto& replace_all(auto& string, auto search, auto replace)
Then here is a code example to test the function:
std::string t = "hello world";
replace_all(t, "hello", []() { return "Hello"; });
replace_all(t, std::string("Hello"), "HELLO");
replace_all(t, "world", std::string("World"));
replace_all(t, "World", []() { return std::string("WORLD"); });
replace_all(t, "WORLD", []() { return "sunshine"; });
replace_all(t, std::string("HELLO"), []() { return "good day"; });
replace_all(t, "&", []() -> std::string { return (const char*)0; }); //never evaluated!
assert(t == "good day sunshine");
Note:
I moved my own solution (which isn't meeting all requirements) with improvements from Pepijn Kramer and Igor Tandetnik to an answer, but I am seeking improvements, consisting only of one function, if possible.
if constexpr