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();
}
}