This function formats a string by replacing every occurrence of '{n}' with the corresponding argument that was passed to the function where n
is an index that starts at 0. The same index can be used multiple times and the order is irrelevant. If n
is out of bounds or cannot be converted to an integer number I just add '{n}' to the result. The function is intended to be used e.g with config files that can be changed by the user where it would be unsafe to use sprintf
. The use of std::ostringstream
allows me to pass any type that has an overloaded <<
operator. I was considering using iterators in the loops but I'm not sure whether that would gain anything.
#pragma once
#include <string>
#include <vector>
#include <sstream>
#include <string_view>
namespace util::str{
inline void to_string_vector(std::vector<std::string>&){} //Dummy used to end recursion
template<typename T, typename ... Args>
void to_string_vector(std::vector<std::string>& result, const T& t, Args&& ...args){
result.push_back((std::ostringstream{} << t).str());
to_string_vector(result, args...);
}
template<typename ... Args>
std::string format(std::string_view fmt, Args&& ...args){
std::string result;
std::vector<std::string> values;
result.reserve(fmt.size());
to_string_vector(values, args...);
for(std::size_t first = 0; first < fmt.size(); ++first){
if(fmt[first] == '{'){
for(std::size_t second = first + 1; second < fmt.size(); ++second){
if(fmt[second] == '}'){
try{
std::size_t index = std::stoi(std::string{fmt.substr(first + 1, second - first - 1)});
if(index < values.size())
result += values[index];
else
result += fmt.substr(first, second - first + 1);
}catch(...){
result += fmt.substr(first, second - first + 1);
}
first = second;
break;
}else if(fmt[second] == '{'){
result += fmt.substr(first, second - first);
first = second - 1;
break;
}
}
}else{
result.push_back(fmt[first]);
}
}
return result;
}
}
ostream
, but it works and looks ok. Any concerns and/or future plans? \$\endgroup\$}
and before{
when there is something else after/before:} else {
andif(fmt[second] == '}') {
, maybe after keywords (if (...
andfor (
) but that would be bonus :) \$\endgroup\$