Created
November 21, 2012 21:07
-
-
Save cjameshuff/4127728 to your computer and use it in GitHub Desktop.
Extensible formatted strings for C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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