Skip to content

Instantly share code, notes, and snippets.

@jfogarty
Created July 21, 2023 18:09
Show Gist options
  • Save jfogarty/2dc0fc2354d2333c75b8f4d4a507d1a5 to your computer and use it in GitHub Desktop.
Save jfogarty/2dc0fc2354d2333c75b8f4d4a507d1a5 to your computer and use it in GitHub Desktop.
C++ templates based safe sprintf
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