Skip to content

Instantly share code, notes, and snippets.

@cjameshuff
Created November 21, 2012 21:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjameshuff/4127728 to your computer and use it in GitHub Desktop.
Save cjameshuff/4127728 to your computer and use it in GitHub Desktop.
Extensible formatted strings for C++
// Usage:
// string s = (fstring("Testing...%d, %f, %d\n")% 1 % 2.0 % 42).str();
// cout << fstring("Testing...%d, %f, %d\n")% 1 % 2.0 % 42;
// cout << fstring("%s%d, %f, %d\n")% "Testing..." % 1 % 2.0 % 42;
// cout << fstring("%s%c%c%c%d, %f, %d\n")% "Testing" % '.' % '.' % '.' % 1 % 2.0 % 42;
// cout << fstring("%s%d, %f, %d\n")% string("Testing...") % 1 % 2.0 % 42;
#ifndef CPPFORMAT_H
#define CPPFORMAT_H
#include <string>
#include <iostream>
#include <list>
#include <stdexcept>
#include <stdarg.h>
struct fstring_outputbuffer {
// If the string implementation uses copy on write or move constructors,
// this should be reasonably efficient.
std::list<std::string> fragments;
size_t length;
fstring_outputbuffer(): length(0) {}
void append_fmt(const char * fstr, ...)
{
std::string bfr(16, '\0');
va_list ap;
va_start(ap, fstr);
int r = vsnprintf(&(bfr[0]), bfr.length(), fstr, ap);
va_end(ap);
if(r + 1 >= sizeof(bfr))
{
// Buffer was too small, try again
bfr.resize(r + 1);// need one extra for the terminating null
va_start(ap, fstr);
r = vsnprintf(&(bfr[0]), r + 1, fstr, ap);
va_end(ap);
}
bfr.resize(r);
if(r < 0)
throw std::invalid_argument("Format error");
fragments.push_back(bfr);
length += r;
}
void append(const std::string & str)
{
fragments.push_back(str);
length += str.length();
}
std::string str() const
{
std::string result;
result.reserve(length);
for(auto & i : fragments)
result.append(i);
return result;
}
};
struct fstring_state {
size_t fstart, fend;
std::string format;
fstring_outputbuffer result;
fstring_state(const std::string & fmt): fstart(0), fend(0), format(fmt) {}
// Advance idx to location of a valid format, return format
std::string next_fmt(const std::string & s)
{
while(1)
{
fstart = format.find('%', fend);
if(fstart == std::string::npos || fstart + 1 == format.length())
throw std::invalid_argument("Format not found");
// concatenate string up to format
result.append(format.substr(fend, fstart - fend));
fend = format.find_first_not_of("0123456789+-.#hlLzjt", fstart + 1);
if(fend == std::string::npos)
throw std::invalid_argument("Invalid format");
++fend;
if(format[fend - 1] == '%')
{
result.append("%");
}
else
{
if(s.find(format[fend - 1]) == std::string::npos)
throw std::invalid_argument("Format mismatch");
break;
}
}
return format.substr(fstart, fend - fstart);
} // fstring::next_fmt()
std::string str() {
result.append(format.substr(fend));
return result.str();
}
};
struct fstring {
mutable fstring_state * state;
fstring(const fstring & rhs): state(rhs.state) {rhs.state = NULL;}
public:
fstring(const std::string & fmt): state(new fstring_state(fmt)) {}
~fstring() {if(state) delete state;}
std::string next_fmt(const std::string & s) const {return state->next_fmt(s);}
template<typename T>
fstring printfmt(const std::string & fmtChars, const T & rhs) const {
std::string fstr = state->next_fmt(fmtChars);
state->result.append_fmt(fstr.c_str(), rhs);
return fstring(*this);
}
fstring append(const std::string & str) const {
state->result.append(str);
return fstring(*this);
}
std::string str() const {return state->str();}
}; // fstring
static inline fstring operator%(const fstring & lhs, const char * rhs) {return lhs.printfmt("s", rhs);}
static inline fstring operator%(const fstring & lhs, const std::string & rhs) {return lhs.printfmt("s", rhs.c_str());}
static inline fstring operator%(const fstring & lhs, char rhs) {return lhs.printfmt("c", rhs);}
static inline fstring operator%(const fstring & lhs, int rhs) {return lhs.printfmt("di", rhs);}
static inline fstring operator%(const fstring & lhs, unsigned int rhs) {return lhs.printfmt("uxXo", rhs);}
static inline fstring operator%(const fstring & lhs, float rhs) {return lhs.printfmt("fFeEgG", rhs);}
static inline fstring operator%(const fstring & lhs, double rhs) {return lhs.printfmt("fFeEgG", rhs);}
static inline fstring operator%(const fstring & lhs, void * rhs) {return lhs.printfmt("p", rhs);}
static inline std::ostream & operator<<(std::ostream & lhs, const fstring & rhs) {
return lhs << rhs.str();
}
#endif // CPPFORMAT_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment