Saturday, December 8, 2012

Safe_Printf

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.

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


No comments:

Post a Comment