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

というわけで,マクロに(あんまり)頼らないバイナリリテラルが出来ました!
これも力技じゃんとか言わないように.