Making a safe printf().
This is quickly stubbed in so I can get the code up for the Ultracode meet up. I'll write a detailed explanation soon. There also seems to be a Microsoft bug in this, so std::string doesn't properly convert to a c style string. This is not a complete example, but finishing it would be a pretty easy task.
This is quickly stubbed in so I can get the code up for the Ultracode meet up. I'll write a detailed explanation soon. There also seems to be a Microsoft bug in this, so std::string doesn't properly convert to a c style string. This is not a complete example, but finishing it would be a pretty easy task.
#pragma once #include <type_traits> #include <string> #include <exception> #include <assert.h> #include <cstdio> #include <iostream> template<class _Ty> struct _is_char : std::false_type {}; template<> struct _is_char<char> : std::true_type {}; template <class _Ty> struct _is_c_string : std::false_type {}; template<class _Ty> struct _is_c_string<_Ty *> : _is_char<typename std::remove_cv<_Ty>::type> {}; template<class _Ty> struct is_c_string : _is_c_string<typename std::remove_cv<_Ty>::type> {}; template <typename T> typename std::enable_if<std::is_integral<T>::value, long>::type normalizeArg(T arg) {std::cout << "LongType/n"; arg;} template <typename T> typename std::enable_if<std::is_floating_point<T>::value, double>::type normalizeArg(T arg) {std::cout << "FloatType/n";return arg;} template <typename T> typename std::enable_if<std::is_pointer<T>::value, T>::type normalizeArg(T arg) {std::cout << "PointerType/n";return arg;} const char* normalizeArg(const std::string& arg) {std::cout << "StringType/n";return arg.c_str();} void check_printf(const char* f) { for (; *f; ++f) { if (*f != '%' || *++f == '%') continue; throw std::exception("Too many format specifiers"); } } template <typename T, typename... Ts> void check_printf(const char* f, const T& t, const Ts&... ts) { for (; *f; ++f) { if (*f != '%' || *++f == '%') continue; switch (*f) { default: throw std::exception("Invalid format"); case 'd': if(!std::is_integral<T>::value) { throw std::exception("T is not an integral"); } break; case 'f': case 'g': if(!std::is_floating_point<T>::value) { throw std::exception("T is not a float"); } break; case 's': if(!is_c_string<T>::value) { throw std::exception("T is not a c string"); } break; } return check_printf(++f, ts...); } throw std::exception("Too few format specifiers"); } template <typename... Ts> int safe_printf(const char* f, const Ts&... ts) { check_printf(f, normalizeArg(ts)...); return printf(f, normalizeArg(ts)...); }
Here is how to use it. Pretty basic.
#include "SafePrintf.h" #include <string> #include <iostream> void main() { std::string str("Try this out."); const int i = 5; float f = 0.123f; try { safe_printf("This is from safe printf. int %d, string %s, and a float %f", i, str, f); } catch(std::exception e) { std::cout << e.what(); } }