binary-literal 0x(笑)
C++にバイナリリテラルが欲しい?
BOOST_BINARYなんてものも有りますが…なに?実装が力業過ぎでイヤ!?
仕方ないなあ.┐(´ー`)┌
と, 言うわけで
// 理想 BOOST_STATIC_ASSERT(( JEFUTY_BINARY(10)==2 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0010)==2 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0010,1010)==42 ));
これを目指します.
つまり, [01]を4桁区切りで並べて2進数として扱えれば良しとしましょう.
とりあえず, 10進数を2進数として変換するメタ関数を書きます.
template<std::size_t N> struct bin { static int const value = (bin<N/10>::value<<1) + N%10; }; template<> struct bin<0> { static int const value = 0; }; template<> struct bin<1> { static int const value = 1; }; BOOST_STATIC_ASSERT((bin<10>::value==2)) BOOST_STATIC_ASSERT((bin<101>::value==5)) BOOST_STATIC_ASSERT((bin<1101>::value==13))
ありがち. 今さらって感じですね.
で,
これを幾つも並べて書きたいので, (私の中で)はやりのVariadicTemplatesを使って,
template<int... NS> struct binary_literal : details::binary_literal_impl< // それぞれの数字にbin<>を適用 jefuty::mpl::seq<NS...>, sizeof...(NS)-1 > { }
こんな感じになればよさげです.
では_implを実装します.
template<typename T, size_t N> struct binary_literal_impl { // 昨日作ったat<N,Seq>を使う static const int value = bin<jefuty::mpl::at<N,T>::value>::value // 四桁ずつ区切られているので4シフト + (binary_literal_impl<T, N-1>::value<<4); }; template<typename T> struct binary_literal_impl<T,0> { static const int value = bin<jefuty::mpl::at<0,T>::value>::value; };
まあこれもありがちですね. というかat便利.
さてこれで目標が達成出来たんでしょうか?使ってみます.
// 現実 cout << binary_literal<1>::value << endl; // 1 cout << binary_literal<1,0>::value << endl; // 16 cout << binary_literal<1,10>::value << endl; // 18 cout << binary_literal<1,10,11>::value << endl; // 291
とっても読みやす……… (・_・?)ん?
全然だめでした….
cout << 010 << endl; // 8
C++では0から始まるリテラルは8進数という誰得罠文法があるわけで,
0埋めして4桁にすると8進数が混ざってしまうです.
オワタ?
…
…
…
しかし!まだイケルっ!
8進リテラルを見直してみる.
// 8進リテラル cout << 0000 << endl; // 0 % 8 = 0 cout << 0001 << endl; // 1 % 8 = 1 cout << 0010 << endl; // 8 % 8 = 0 cout << 0011 << endl; // 9 % 8 = 1 cout << 0100 << endl; // 64 % 8 = 0 cout << 0101 << endl; // 65 % 8 = 1 // !?
[01]しか使わないので, 8進リテラルの値は必ず8(のべき乗)で割ると0または1余ります!!
一方10進数リテラルの場合は, 10(のべき乗)で割ると0または1余ります!!
つまりどちらか区別出来る!
// 8進か10進かを区別する実装 template<typename T, size_t N> struct binary_literal_impl2 { private: static const int hd = jefuty::mpl::details::at<N,T>::value; public: static const int value = ((hd%10==0 || hd%10==1) ? bin<hd>::value // 10進を2進数に : oct2bin<hd>::value) // 8進を2進数に + (binary_literal_impl2<T, N-1>::value<<4); }; template<typename T> struct binary_literal_impl2<T,0> { private: static const int hd = jefuty::mpl::details::at<0,T>::value; public: static const int value = (hd%10==0 || hd%10==1) ? bin<hd>::value : oct2bin<hd>::value ; };
8進数を2進数の値に直すメタ関数
template<std::size_t N> struct oct2bin { static int const value = (oct2bin<N/8>::value<<1) + N%8; }; template<> struct oct2bin<0> { static int const value = 0; }; template<> struct oct2bin<1> { static int const value = 1; };
あとはこれをマクロに突っ込むと…
#define JEFUTY_BINARY(...) JEFUTY_BINARY_I(__VA_ARGS__) #define JEFUTY_BINARY_I(...) (jefuty::binary_literal2<__VA_ARGS__>::value) BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001)==1 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(10)==2 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001,0000)==16 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001,1010)==26 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001,0010,0011)==291 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001, 10,0011)==291 )); BOOST_STATIC_ASSERT(( JEFUTY_BINARY(0001,1000,0011)==256+128+3));
やたーw
というわけで,マクロに(あんまり)頼らないバイナリリテラルが出来ました!
これも力技じゃんとか言わないように.