Created
July 21, 2023 18:09
-
-
Save jfogarty/2dc0fc2354d2333c75b8f4d4a507d1a5 to your computer and use it in GitHub Desktop.
C++ templates based safe sprintf
This file contains hidden or 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
using CString = const char*; | |
using String = std::string; | |
//----------------------------------------------------------------------------- | |
// Template based sprintf replacement | |
//----------------------------------------------------------------------------- | |
class TSException : public std::runtime_error { | |
public: | |
TSException() : std::runtime_error("Typed String format exception") { } | |
TSException(CString what) : std::runtime_error(what) { } | |
TSException(String what) : std::runtime_error(what) { } | |
TSException(std::stringstream& what) : std::runtime_error(what.str()) { } | |
}; | |
//----------------------------------------------------------------------------- | |
inline void TSSprintf(std::stringstream &ss, const char* format) | |
{ | |
while (*format != '\0') { | |
char fc = *format; | |
if (fc == '%' && *(format+1) == '%') format++; | |
ss << fc; format++; | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
#define _TSSPRINTF_QUALIFIERS \ | |
format++; fc = *format; \ | |
bool has_width = false; \ | |
bool has_precision = false; \ | |
bool is_left = false; \ | |
bool is_hex = false; \ | |
int width = 0; \ | |
int precision = 0; \ | |
if (fc == '-') { \ | |
ss << std::setiosflags(std::ios::left); \ | |
format++; fc = *format; is_left = true; \ | |
} \ | |
if (fc == '0') { \ | |
ss << std::setfill('0'); \ | |
format++; fc = *format; \ | |
} \ | |
while (fc >= '0' && fc <= '9') { \ | |
width = width*10 + (fc-'0'); \ | |
format++; fc = *format; \ | |
has_width = true; \ | |
} \ | |
if (fc == '.') { \ | |
has_precision = true; \ | |
format++; fc = *format; \ | |
while (fc >= '0' && fc <= '9') { \ | |
precision = precision*10 + (fc-'0'); \ | |
format++; fc = *format; \ | |
} \ | |
} \ | |
#define _TSSPRINTF_FORMATS \ | |
if (fc == 'l') { format++; fc = *format; } \ | |
if (fc == 'l') { format++; fc = *format; } \ | |
if (fc == 'h') { format++; fc = *format; } \ | |
if (fc == 'h') { format++; fc = *format; } \ | |
if (fc == 's') {} \ | |
else if (fc == 'i' || fc == 'd' || fc == 'u') {} \ | |
else if (fc == 'p' || fc == 'c') {} \ | |
else if (fc == 'f') { \ | |
ss << std::fixed << std::setprecision(2); \ | |
} \ | |
else if (fc == 'x') { \ | |
ss << std::hex << std::nouppercase; is_hex = true; \ | |
} \ | |
else if (fc == 'X') { \ | |
ss << std::hex << std::uppercase; is_hex = true; \ | |
} \ | |
else { \ | |
std::stringstream es; es << "Unexpected format: '%" << fc \ | |
<< "' in '" << format << "'"; \ | |
throw TSException(es); \ | |
} \ | |
#define _TSSPRINTF_APPLY_QUALIFIERS \ | |
if (has_width) {ss<<std::setw(width); } \ | |
if (has_precision) {ss<<std::setprecision(precision); } \ | |
ss << value; \ | |
if (is_left) {ss<<std::setiosflags(std::ios::right); } \ | |
if (is_hex) {ss<<std::dec; } \ | |
if (has_width) {ss<<std::setfill(' '); } | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
template<typename T, typename... Targs> | |
void TSSprintf(std::stringstream &ss, const char* format, T value, Targs... Fargs) | |
{ | |
while (*format != '\0') | |
{ | |
char fc = *format; | |
if (fc == '%') { | |
if (*(format+1) == '%' || *(format+1) == ' ') { | |
fc = '%'; format++; | |
} | |
else { | |
_TSSPRINTF_QUALIFIERS; | |
_TSSPRINTF_FORMATS; | |
_TSSPRINTF_APPLY_QUALIFIERS; | |
TSSprintf(ss, format + 1, Fargs...); | |
return; | |
} | |
} | |
ss << fc; | |
format++; | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
template<typename T, typename... Targs> | |
String TSprintf(const char* format, T value, Targs... Fargs) | |
{ | |
std::stringstream ss; | |
while (*format != '\0') | |
{ | |
char fc = *format; | |
if (fc == '%') { | |
if (*(format+1) == '%') { | |
fc = '%'; format++; | |
} | |
else { | |
_TSSPRINTF_QUALIFIERS; | |
_TSSPRINTF_FORMATS; | |
_TSSPRINTF_APPLY_QUALIFIERS; | |
TSSprintf(ss, format + 1, Fargs...); | |
return ss.str(); | |
} | |
} | |
ss << fc; | |
format++; | |
} | |
return ss.str(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment