コンパイラ電卓
すごく実用性のないものを作った
#include <iostream> //a + b template <int a,int b> struct Adder { enum {value = a + b }; }; //num! template <int num,int unused> struct Factor { enum {value = num * Factor<num - 1,unused>::value}; }; template <int unused> struct Factor<0,unused> { enum {value = 1}; }; template <int num,int power> struct Power { enum {value = num * Power<num,power - 1>::value}; }; template <int num> struct Power<num,0> { enum {value = num}; }; //a % b template <int a,int b> struct Remainder { enum {value = A % B}; }; //a / b template <int a,int b> struct Divide { enum {value = a / b}; }; //a * b template <int a,int b> struct Multiple { enum {value = a * b}; }; int main() { #ifndef A #define A 1 #endif #ifndef B #define B 1 #endif std::cout << STAT<A,B>::value; return 0; }
コンパイル時にマクロで引数を与えてやると
c:\Users\hiroya\Documents\Visual Studio 2008\Projects\TestProject>cl main.cpp -D STAT=Adder -DA=1 -DB=2 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. main.cpp C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\xlocale(342) : warning C 4530: C++ 例外処理を使っていますが、アンワインド セマンティクスは有効にはなりま せん。/EHsc を指定してください。 Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:main.exe main.obj c:\Users\hiroya\Documents\Visual Studio 2008\Projects\TestProject>main.exe 3 c:\Users\hiroya\Documents\Visual Studio 2008\Projects\TestProject>cl main.cpp -D STAT=Power -DA=2 -DB=5 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. main.cpp C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\xlocale(342) : warning C 4530: C++ 例外処理を使っていますが、アンワインド セマンティクスは有効にはなりま せん。/EHsc を指定してください。 Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:main.exe main.obj c:\Users\hiroya\Documents\Visual Studio 2008\Projects\TestProject>main.exe 64
ちなみに、アセンブラファイルを出力させてみると
; 1035 : std::cout << STAT<A,B>::value; 00003 6a 40 push 64 ; 00000040H 00005 8b 0d 00 00 00 00 mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A 0000b ff 15 00 00 00 00 call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
ということで、生成される実行ファイルはちゃんと定数をpushするだけになってる。
もともとはsinとかcosとかをコンパイル時に実行できたらいいなと思って書いていたので、
↓みたいなものも書いてたんだけど
template <double num,int recursionCount> struct Sin { enum {value = Sin<num,recursionCount - 1>::value + (!recursionCount) * (1 / (Factor<recursionCount * 2 + 1,1>::value)) * Power<num,recursionCount * 2 + 1>::value}; }; template <double num> struct Sin<num,0> { enum {value = num}; };
書き終わってから、「コンパイル時に小数って普通にやったら駄目なんじゃないの」ってことに気づいてみたり。
なので上のコードは一度も実行してないというか、できないというか、デバッグもできてないしもしかしたら再帰がとまらないかもしれない。
どうやらコンパイル時に小数を扱うのは、Boost/MPLにちょっとあるみたいなんだけど、実装ほぼ空だった。
BoostVaultにはもうちょっとましなのがあるらしいので、試してみようかな。