printfに型チェックを付ける
どうもコンパイラ拡張で文字列を型パラメータとして取れるユーザ定義リテラルがあるようなので,printfに型チェックを付けるようなのを書いてみた. 桁数表示とか何も実装してないけれど,雰囲気がそれっぽくなってきたあたりで飽きた.もっと賢い実装方法があるように思う.あと地味にrequiresなんてconceptカッコつけて使ってみたり.
#include <iostream> #include <tuple> #include <type_traits> #include <utility> namespace detail { template <char C> struct format {}; template <> struct format<'d'> { using type = int; }; template <> struct format<'c'> { using type = char; }; template <> struct format<'s'> { using type = const char*; }; template <> struct format<'f'> { using type = float; }; template <typename Str, typename F, typename B, char... Chars> struct formatter { using type = std::tuple<Str, F>; }; template <typename Str, typename... Fs, bool B, bool... Bs, char C, char... Chars> requires B struct formatter<Str, std::tuple<Fs...>, std::integer_sequence<bool, B, Bs...>, C, Chars...> { using type = typename formatter< Str, std::tuple<Fs..., typename format<C>::type>, std::integer_sequence<bool, Bs...>, Chars...>::type; }; template <typename Str, typename... Fs, bool B, bool... Bs, char C, char... Chars> requires not B struct formatter<Str, std::tuple<Fs...>, std::integer_sequence<bool, B, Bs...>, C, Chars...> { using type = typename formatter< Str, std::tuple<Fs...>, std::integer_sequence<bool, Bs...>, Chars...>::type; }; template <typename... Types> struct and_ { static constexpr bool value = false; }; template <typename... Types> struct and_<Types*...> { static constexpr bool value = true; }; } template <bool... Bs> struct and_ { static constexpr bool value = detail::and_< typename std::conditional<Bs, int*, int>::type...>::value; }; template <char... Chars> struct formatter : detail::formatter< std::integer_sequence<char, Chars...>, std::tuple<>, std::integer_sequence<bool, false, (Chars == '%')...>, Chars..., '\0'> {}; template <typename Char, Char... Chars> constexpr auto operator ""_fmt() { return typename formatter<Chars...>::type{}; } template <char... Chars, typename... Types, typename... UTypes> void print(const std::tuple<std::integer_sequence<char, Chars...>, std::tuple<Types...>>&, UTypes&&... args) { static_assert(and_<std::is_convertible<std::decay_t<Types>, std::decay_t<UTypes&&>>::value...>::value); const char str[sizeof...(Chars)] = { Chars... }; std::printf(str, std::forward<UTypes>(args)...); } int main() { print("%d %d %f %s\n"_fmt, 10, 20, 3.14f, "hoge"); }