文字列リテラルから型文字列を作る1
これまで数多くの人が型文字列の生成において<'h','e','l','l','o'>などの入力で深い悲しみを感じて来ました。
C++11で生まれたユーザ定義リテラルは、数値からであれば型文字列を生成出来ますが文字列リテラルには適用できません。
以前プリプロセッサで実装したことがありますが、今回はC++14なTMPで挑戦してみました。
ただし文字コードなんかの環境依存に関して考慮してないのでネタ程度にお願いします。
#include <type_traits> #include <cstdint> #include <boost/mpl/string.hpp> namespace mpl = boost::mpl; namespace type_str { template <std::uint64_t str, std::size_t length, std::size_t step = 0, bool finish = (length - 1 <= step), char... chars> struct to_string_impl { using type = mpl::string<chars...>; }; template <std::uint64_t str, std::size_t length, std::size_t step, char... chars> struct to_string_impl<str, length, step, false, chars...> : to_string_impl<str, length, step + 1, length - 1 <= step, static_cast<char>((str & (static_cast<std::uint64_t>(0b11111111) << (8 * step))) >> (8 * step)), chars...> {}; template <std::uint64_t str, std::size_t N = 0, bool finish = ((str >> 8 * N) & 0b11111111) == 0> struct to_string { using type = typename to_string_impl<str, N - 1>::type; }; template <std::uint64_t str, std::size_t N> struct to_string<str, N, false> : to_string<str, N + 1, ((str >> 8 * N) & 0b11111111) == 0> {}; constexpr auto operator "" _s(char const* str, std::size_t size) { std::uint64_t bits = 0; for(std::size_t i = 0; i < size; ++i) { bits = bits << 8; bits = bits | str[i]; } return bits; } } int main() { using namespace type_str; static_assert(std::is_same<to_string<"kuso"_s>::type, mpl::string<'k','u','s','o'>>::value, ""); }
アプローチとしては、ユーザ定義リテラルを用いて文字列リテラルを、1文字目の8bit, 2文字目の8bit ... n文字目の8bitが連結されたunsigned long long int型の整数値にエンコード。 TMPを用いて、文字列から文字数を取得し、更にunsined long long int型の値を型文字列にデコードしています。文字数の上限はstd::uint64_tを使っているので7文字です。
to_string<"kuso"_s>::typeで、mpl::string<'k','u','s','o'>と等価の型を得られます。
正直、かなりがっかり感ある実装になってしまいました。 文字列リテラルをcharのtemplate parameter packとして受け取れるユーザ定義リテラルが規格に欲しいですね。