This, of course, can be written in C ++ 11 with variable templates. Itβs best to wrap what already exists than to try to write everything yourself. If you are already using Boost, it is quite simple to wrap boost::format as follows:
#include <boost/format.hpp> #include <string> namespace details { boost::format& formatImpl(boost::format& f) { return f; } template <typename Head, typename... Tail> boost::format& formatImpl( boost::format& f, Head const& head, Tail&&... tail) { return formatImpl(f % head, std::forward<Tail>(tail)...); } } template <typename... Args> std::string format( std::string formatString, Args&&... args) { boost::format f(std::move(formatString)); return details::formatImpl(f, std::forward<Args>(args)...).str(); }
You can use it the way you want:
std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
If you don't want to use Boost (but you really need to), you can also wrap snprintf . This is a bit more attractive and error prone, since we need to manage the char buffers and the old argument of non-type variable length arguments. It gets a little cleaner using unique_ptr 's:
#include <cstdio> // snprintf #include <string> #include <stdexcept> // runtime_error #include <memory> // unique_ptr namespace details { template <typename... Args> std::unique_ptr<char[]> formatImplS( size_t bufSizeGuess, char const* formatCStr, Args&&... args) { std::unique_ptr<char[]> buf(new char[bufSizeGuess]); size_t expandedStrLen = std::snprintf(buf.get(), bufSizeGuess, formatCStr, args...); if (expandedStrLen >= 0 && expandedStrLen < bufSizeGuess) { return buf; } else if (expandedStrLen >= 0 && expandedStrLen < std::numeric_limits<size_t>::max()) { // buffer was too small, redo with the correct size return formatImplS(expandedStrLen+1, formatCStr, std::forward<Args>(args)...); } else { throw std::runtime_error("snprintf failed with return value: "+std::to_string(expandedStrLen)); } } char const* ifStringThenConvertToCharBuf(std::string const& cpp) { return cpp.c_str(); } template <typename T> T ifStringThenConvertToCharBuf(T const& t) { return t; } } template <typename... Args> std::string formatS(std::string const& formatString, Args&&... args) { // unique_ptr<char[]> calls delete[] on destruction std::unique_ptr<char[]> chars = details::formatImplS(4096, formatString.c_str(), details::ifStringThenConvertToCharBuf(args)...); // string constructor copies the data return std::string(chars.get()); }
There are some differences between snprintf and boost::format in terms of the format specification, but your example works with both.
isarandi
source share