As a small exercise I have written some string formatting and printing functions in C++11. I would like a bit of code review, not so much on the merits of using this over something like std::stringstream
or boost::format
, but simply whether or not my implementation makes sense. I realize that simply performing string replacements isn't that robust and a more sophisticated version would actually parse the format string. I especially would like to know if my use of things like rvalue references, std::forward
, std::move
and variadic templates are correct, as these are the main features I was focusing on in this exercise.
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
#include <utility>
#include <cstdio>
namespace detail
{
template <unsigned N, class T>
void format_impl(std::string& str, T&& item)
{
char find[8];
snprintf(find, 8, "{%d}", N);
auto replace = boost::lexical_cast<std::string>(std::forward<T>(item));
boost::replace_all(str, find, replace);
}
template <unsigned N, class Head, class... Tail>
void format_impl(std::string& str, Head&& head, Tail&&... tail)
{
detail::format_impl<N>(str, std::forward<Head>(head));
detail::format_impl<N+1>(str, std::forward<Tail>(tail)...);
}
}
template <class... Args>
std::string format(const std::string& fmt, Args&&... args)
{
std::string temp = fmt;
detail::format_impl<0>(temp, std::forward<Args>(args)...);
return temp;
}
template <class... Args>
std::string format(std::string&& fmt, Args&&... args)
{
std::string temp = std::move(fmt);
detail::format_impl<0>(temp, std::forward<Args>(args)...);
return temp;
}
template <class... Args>
void print(const std::string& fmt, Args&&... args)
{
std::cout << format(fmt, std::forward<Args>(args)...);
}
template <class... Args>
void print(std::string&& fmt, Args&&... args)
{
std::cout << format(std::forward<std::string>(fmt), std::forward<Args>(args)...);
}
int main()
{
print("Hello, {0}! The answer is {1}.\n", "World", 42);
}
const T&
andT&&
all over the place?T&&
implicitly allowsT&
andconst T&
due to reference collapsing. \$\endgroup\$