-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Check log arguments for correctness #2
Comments
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/utility/string_ref.hpp>
template<std::size_t N>
constexpr bool checkValidFormats(const char (&fmt)[N], size_t n, char c)
{
return n >= N ?
false
: fmt[n] == c ?
true
: checkValidFormats(fmt, n + 1, c);
}
template<class>
struct FormatSupportedType;
#define SUPPORTED_TYPE(T, Fmts) \
template<> \
struct FormatSupportedType<T> \
{ \
constexpr static bool supports(char c) \
{ return checkValidFormats(Fmts, 0, c) \
? true : throw std::logic_error("invalid fmt for type"); } \
}
SUPPORTED_TYPE(char, "c");
SUPPORTED_TYPE(int, "d*");
SUPPORTED_TYPE(unsigned, "u*");
SUPPORTED_TYPE(char*, "s");
SUPPORTED_TYPE(const char*, "s");
SUPPORTED_TYPE(std::string, "s");
SUPPORTED_TYPE(boost::string_ref, "s");
SUPPORTED_TYPE(double, "f");
SUPPORTED_TYPE(float, "f");
/////////////////
constexpr bool isDigit(char c)
{
return c >= '0' && c <= '9';
}
constexpr bool isModifier(char c)
{
return c == 'l' ||
c == 'h' ||
c == 'j' ||
c == 'z' ||
c == 't' ||
c == 'L' ||
c == '#' ||
c == '+' ||
c == '-' ||
c == ' ' ||
c == '\'' ||
c == 'I' ||
c == '.' ||
c == '=' ||
isDigit(c);
}
template<std::size_t N>
constexpr size_t nextNonModifier(const char (&fmt)[N], std::size_t n)
{
return
n >= N ?
throw std::logic_error("invalid format string")
: isModifier(fmt[n]) ?
nextNonModifier(fmt, n + 1)
: n;
}
////////////////////
template<std::size_t N>
constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n);
template<std::size_t N, class T, class... Ts>
constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n, const T& arg, const Ts&... args);
////////////////////
template<std::size_t N, typename T1, typename T2, typename T3, typename... Ts>
constexpr auto checkWidthAndPrecision(const char (&fmt)[N], std::size_t n, const T1& /*width*/, const T2& /*precision*/, const T3& /* arg */, const Ts&... args)
-> typename std::enable_if<
std::is_integral<T1>::value &&
std::is_integral<T2>::value,
bool>::type
{
return FormatSupportedType< typename std::decay<T3>::type>::supports(fmt[n]) &&
checkFormatHelper(fmt, n + 1, args...);
}
template<std::size_t N, typename... Ts>
constexpr bool checkWidthAndPrecision(const char (&)[N], std::size_t, const Ts&...)
{
return false;
}
////////////////////
template<std::size_t N, typename T1, typename T2, typename... Ts>
constexpr auto checkWidthOrPrecision(const char (&fmt)[N], std::size_t n, const T1& /*precision*/, const T2& /* arg */, const Ts&... args)
-> typename std::enable_if<
std::is_integral<T1>::value,
bool>::type
{
return FormatSupportedType< typename std::decay<T2>::type>::supports(fmt[n]) &&
checkFormatHelper(fmt, n + 1, args...);
}
template<std::size_t N, typename... Ts>
constexpr bool checkWidthOrPrecision(const char (&)[N], std::size_t, const Ts&...)
{
return false;
}
////////////////////
template<std::size_t N>
constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n)
{
return
n>= N ?
true
: fmt[n] != '%' ?
checkFormatHelper(fmt, n + 1)
: fmt[n + 1] == '%' ?
checkFormatHelper(fmt, n + 2)
: false;
}
template<std::size_t N, class T, class... Ts>
constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n, const T& arg, const Ts&... args)
{
return
n >= N ?
throw std::logic_error("too many arguments for provided format string")
: fmt[n] != '%' ?
checkFormatHelper(fmt, n + 1, arg, args...)
// literal percent character
: (fmt[n + 1] == '%') ?
checkFormatHelper(fmt, n + 2, arg, args...)
// long-long modifier
: (fmt[n + 1] == 'l' && fmt[n + 2] == 'l') ?
FormatSupportedType< typename std::decay<T>::type >::supports(fmt[n + 3]) &&
checkFormatHelper(fmt, n + 4, args...)
// width & precision modifier
: (fmt[n + 1] == '*' && fmt[n + 2] == '.' && fmt[n + 3] == '*') ?
checkWidthAndPrecision(fmt, n + 4, arg, args...)
// width or precision modifier
: ((fmt[n + 1] == '.' && fmt[n + 2] == '*') || (fmt[n + 1] == '*')) ?
checkWidthOrPrecision(fmt, (fmt[n + 1] == '.' ? n + 3 : n + 2), arg, args...)
// other modifier
: (isModifier(fmt[n + 1])) ?
FormatSupportedType< typename std::decay<T>::type>::supports(fmt[nextNonModifier(fmt, n + 2)]) &&
checkFormatHelper(fmt, nextNonModifier(fmt, n + 2) + 1, args...)
// no modifier
: FormatSupportedType< typename std::decay<T>::type>::supports(fmt[n + 1]) &&
checkFormatHelper(fmt, n + 2, args...);
}
template<std::size_t N, class... Ts>
constexpr bool checkFormat(const char (&fmt)[N], const Ts&... args)
{
return checkFormatHelper(fmt, 0, args...);
}
// printing...
void add(boost::format&)
{ }
template<typename T, typename... Ts>
void add(boost::format& f, const T& arg, const Ts&... ts)
{
f % arg;
add(f, ts...);
}
#define LOG(fmt, ...) \
{ \
static_assert(checkFormat(fmt, ##__VA_ARGS__), "Format is incorrect"); \
boost::format f(fmt); \
add(f, ##__VA_ARGS__); \
std::cout << f.str() << std::endl; \
}
int main()
{
// char
LOG("%c", 'x');
// integral
LOG("%d", -123);
LOG("%ld", -123);
LOG("%u", 123u);
LOG("%lu", 123u);
// strings
LOG("%s", "hello world");
{ const char* s = "hello world"; LOG("%s", s); }
{ std::string s = "hello world"; LOG("%s", s); }
{ std::string s = "hello world"; boost::string_ref r(s); LOG("%s", r); }
// floating point
LOG("%f", 1.23);
LOG("%f", 1.23f);
// width / precision
LOG("%02d", 1);
LOG("%.2d", 123);
LOG("% 3s", "hello");
LOG("% 3s", "yo");
LOG("%.3s", "hello");
LOG("%.3s", "yo");
// incorrect format string
// LOG("%f", 1);
// LOG("%d", 1.23);
// not supported by boost::format
// LOG("%*s", 3, "yo");
// LOG("%*d", 3, 12);
// LOG("%.*s", 3, "hello");
// LOG("%.*d", 3, 12345);
// LOG("%*.*s", 3, 3, "hello");
// LOG("%*.*d", 3, 3, 12345);
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
C++ __attribute__((format(function, string-index, first-to-check)))
The text was updated successfully, but these errors were encountered: