#include "templ_literal.h" #include #include template< class > struct FormatSupportedType; #define SUPPORTED_TYPE(C, T) \ template<> struct FormatSupportedType< T > \ { \ constexpr static bool supports(char c) \ { return ( c == C ); } \ } SUPPORTED_TYPE('c', char); SUPPORTED_TYPE('d', int); template< std::size_t N > constexpr bool checkFormatHelper( const char (&format)[N], std::size_t current ) { return current >= N ? true : format[current] != '%' ? checkFormatHelper( format, current + 1 ) : format[current + 1] == '%' ? checkFormatHelper( format, current + 2 ) : false; } template< std::size_t N, class T, class... Ts > constexpr bool checkFormatHelper( const char (&format)[N], std::size_t current, const T& arg, const Ts & ... args ) { return current >= N ? false : format[current] != '%' ? checkFormatHelper( format, current + 1, arg, args... ) : (format[current] == '%' && format[current + 1] == '%') ? checkFormatHelper( format, current + 2, arg, args... ) : FormatSupportedType< T >::supports(format[current + 1]) && checkFormatHelper( format, current + 2, args... ); } template< std::size_t N, class... Ts > constexpr bool checkFormat( const char (&format)[N], const Ts & ... args ) { return checkFormatHelper( format, 0, args... ); } #define safe_printf_1(FORMAT, ...) \ static_assert(checkFormat( FORMAT, __VA_ARGS__ ), "Format is incorrect"); \ printf(FORMAT, __VA_ARGS__) template< char... FORMAT, class... ARGS > int safe_printf_2(TemplateLiteral, ARGS... args) { constexpr char format[] = {FORMAT... , '\0'}; static_assert( checkFormat(format, args... ), "Format is incorrect"); return printf( format, args... ); } int main() { safe_printf_1("%c %d", 'x', 257); safe_printf_2(TEMPLATE_LITERAL("%c %d"), 'x', 257); }