プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

今更ながら、文字列を型にしたい

 やーやーやー

 なんで私、最近こんな黒魔術に手を出してるんだろうね。最近、相当変態的な方向性でC++を悪用 利用しようと企ててるから、こんな羽目になってますよ。

 と言うのもですね、こう、色々と面倒くさい諸問題を回避しようとしてたら、黒い方法を思いついてしまったんだ。いや、私は悪くない。Goサインを出した人が悪いんだ。

 

 しかし、C++11、C++14で相当敷居が下がったって言っても、型だとか、constexprってやっぱり、こう、闇の軍団か中3女子的なイメージあるじゃない。タイトルみたいなことは、Sprout使ったらすぐできそうなんだけど、政治的ごにょごにょにより…

 ポンコツ…いや、豚骨な私が手を出して良い領域じゃないと思うんですよ。私はC++を完全に理解した日は来るのだろうか。

 闇の軍団的にはできて当たり前なのかも知れないけど、こう、ファイアをやっと唱えられましたレベルの私だと中々ね。

 

やりたいこと

 以下の型を宣言したい。

template<char... chars>struct char_type
{
  static void print(){
    (void)std::initializer_list<int>{(void(std::cout << chars),0)...};
    std::cout << std::endl;
  }
};

constexprな関数から作る場合

 C++14なら豚骨にも簡単にできた。

 関数から作ることができるので、constexprな配列の名前が定義されてたら、同じように作ることが可能。

template<int N> struct make_alphabet_type
{
private:
  static constexpr char get(int n){return (char)('a'+n);}
  template<typename Seq>struct make_char_type{};
  template<typename I,I... seq>struct make_char_type<std::integer_sequence<I,seq...>>{
    using type = char_type<get(seq)...>;
  };
public:
  using type =typename make_char_type<std::make_integer_sequence<size_t,N>>::type;
};

int main(int argc, char *argv[])
{
  using a5_t = make_alphabet_type<5>::type;
  a5_t::print(); // abcdeと出力される
  return 0;
}

 実体は何処にもないけど、constexprの計算で作ったchar配列の型を作ることができたよ!

 

文字列リテラルから作りたい

 GCC拡張のtemplateユーザー文字列リテラルが使えるなら簡単なんだけど、これどうすれば良いんだろ。一度どこかに名前を作れれたりすればいいんだけど。ラムダで何とかならないかと思ったけど無理でした。

 BOOST_PP_ENUMとか使えばできるっぽいけど。

 BOOST_PP_ENUMと同じことを手でやってしまえば、固定長だけど、出来ないことは無くて、しかも私の目的としてはこれで十分なんだけど。

//GCCの場合はこれ
template<char... chars>
constexpr char_type<chars...> operator ""_chartype(){return char_type<chars...>();}

template<char... chars>struct _char_type{
private:
  static constexpr char str[] = {chars...};
  static constexpr size_t array_size = sizeof...(chars);
  static constexpr size_t count(size_t i){
    return i == array_size? array_size:
      str[i] == '\0' ? i : count(i+1);
  }
  
  static constexpr size_t length = count(0);
  
  template<typename seq>struct make_char_type{};
  template<typename I,I... seq>struct make_char_type<std::integer_sequence<I,seq...>>{
    using type = char_type<str[seq]...>;
  };
public:
  using type = typename make_char_type<std::make_integer_sequence<int, length>>::type;
};
template<int N>
constexpr char get_char(const char (&array)[N],int index){
  return index < N? array[index] : '\0';
}

#define GET_CHAR_TYPE(x) _char_type<get_char(x,0),get_char(x,1),get_char(x,2),get_char(x,3),get_char(x,4),get_char(x,5),get_char(x,6)>::type

int main(int argc, char *argv[])
{
  using abc_t = GET_CHAR_TYPE("abc");
  abc_t::print();
  return 0;
}