Currently I am working on a function similar to the String.Format(...)
function from C#, just in C++. (String.Format(...))
But that's not my problem. The function works fine but problematic is that it takes a vector<string>
as parameter and if I want to use an integer as parameter, I must write code like this:
// function prototype, the function body is not relevant here
string format(string str, vector<string> variables);
// ... some context
// i could use to_string() here,
// but imagine a complex type which only overrides the stream operator
int a = 20;
stringstream ss;
ss << a;
string a_str = format("a has the value '{}'", { ss.str() });
That's quite some boilerplate code!
Thus I need a function which converts a collection of unknown data types into a vector<string>
.
I tried a few things like this:
vector<string> vec_string(vector<void*> args) {
vector <string> result;
for (unsigned i = 0; i < args.size(); i++)
{
stringstream ss;
// I can't dereference an object without knowing to pointer type. :(
ss << *((int*)args[i]);
result.push_back(ss.str());
}
return result;
}
// ... some context
int a = 10;
cout << format("some int: '{}'", vec_string({ (void*) &a }));
Which obviously only works for integer and is very uncomfortable. I feel like the only way to do this is a variadic macro but I got no idea how they work.
here is a link to my format(...)
method.
I am sorry about my spelling, but I tried my best correcting it.
This can be done relatively easily with variadic templates:
template <class T>
auto toString(T&& t) {
std::stringstream s;
s << std::forward<T>(t);
return s.str();
}
template <class... T>
auto toStringVector(T&&... args) {
std::vector<std::string> res {toString(std::forward<T>(args))...};
return res;
}
This will convert each parameter to std::string
via a stringstream
and then return an std::vector<std::string>
containing said strings. (Live example.)
You can then use this straight forward as intended in the question, that is:
std::cout << format("some text", toStringVector(any, number, of, arguments,
of, any, type));
If you are using Boost, you can skip the toString
helper in favor of boost::lexical_cast
:
template <class... T>
auto toStringVector(T&&... args) {
std::vector<std::string> res { boost::lexical_cast<std::string>(std::forward<T>(args))...};
return res;
}
The lexical_cast
will most likely be faster on built-in types.