Reputation: 31
I'm looking for a way to format a string with a variable-size vector of variables. What do you suggest is the best way of doing this?
I already know about std::snprintf and std::vsnprintf but unfortunately none works out of the box for my problem. Also a solution with recursive templates wont work for me because I can't rely on the input format being fully defined at compile time.
Here is a sample interface for the function I'm trying to implement.
std::string format_variable_size(const char* format, const std::vector<int>& in) {
std::string out{};
....
return out;
}
Example input and output:
const char* format = "My first int is %d, my second int is: %d, my float is: %d";
std::vector<int> in = {1,2,3};
the format_variable_size would return
out = "My first int is 1, my second int is: 2, my float is: 3"
Another example:
const char* format = "My first int is %d, my second int is: %d";
std::vector<int> in = {1,2};
the format_variable_size would return
"My first int is 1, my second int is: 2"
Thanks,
Upvotes: 0
Views: 1528
Reputation: 42861
Not pretty solution, since we can't get std::vector
's size at compile time:
template <std::size_t... I>
std::string
format_variable_size_impl(const char* format, const std::vector<int>& in,
std::index_sequence<I...>)
{
// Determine the necessary buffer size
auto size = std::snprintf(nullptr, 0, format, in[I]...);
std::string out(size + 1, 0);
std::sprintf(out.data(), format, in[I]...);
return out;
}
std::string
format_variable_size(const char* format, const std::vector<int>& in)
{
if (in.size() == 0)
return format;
if (in.size() == 1)
return format_variable_size_impl(format, in, std::make_index_sequence<1>{});
if (in.size() == 2)
return format_variable_size_impl(format, in, std::make_index_sequence<2>{});
if (in.size() == 3)
return format_variable_size_impl(format, in, std::make_index_sequence<3>{});
if (in.size() == 4)
return format_variable_size_impl(format, in, std::make_index_sequence<4>{});
if (in.size() == 5)
return format_variable_size_impl(format, in, std::make_index_sequence<5>{});
// ...
}
Upvotes: 0
Reputation: 2624
If the only specifier you use is %d
, then you can easily do a loop and manually replace with the value coming from the vector. Alternatively, you might consider defining your own replacement token (for example ###
) to simplify parsing.
Also, if you can live with relatively small vector size (say maximum numbers), you could simply do something like this:
std::vector<int> copy(in);
copy.resize(10);
std::snprintf(buffer, buffer_size,
copy[0], copy[1], copy[2], copy[3], copy[4],
copy[5], copy[6], copy[7], copy[8], copy[9]);
%d
is greater than the input vector and up to the size of the copy, extra %d will be outputted with 0.Upvotes: 0
Reputation: 4991
If you have nothing against using fmt, I think the following might work :
#include <numeric>
std::string format_variable_size(const char* fmt, std::vector<int> args){
return std::accumulate(
std::begin(args),
std::end(args),
std::string{fmt},
[](std::string toFmt, int arg){
return fmt::format(toFmt, arg);
}
);
}
std::vector<int> v = {1,2,3};
std::cout << format_variable_size("[{}, {}, {}]\n", v);
Upvotes: 2