- 追加された行はこの色です。
- 削除された行はこの色です。
a
いずれ困ったりするときの役に立てばいいかな程度の雑記です
知っといても損はないようなマニアックな知識をご提供(?)
ちなみに、ある程度の知識がないとわからないかもしれない
実際に動かすのがたぶん一番です。
プリプロセッサ応用編
プリプロセッサというのは
#define なんとか
#include <ファイル名>
のように#が先頭に付いている奴ら
こいつらは実はコンパイラでは処理されないんです
(ナ、ナンダッテー)
コンパイラの前にプリコンパイラというものがあります。
プリコンパイラがこのプリプロセッサを展開(置き換え)してくれるのです。
コンパイラによる処理はそのあとです。
使い方次第ではなんかいろいろ化けそうな奴らです
#define A
Aを定義する
#define A B
AをBと定義する
関数(マクロ)を定義することもできる
#define Add(x,y) ((x) + (y))
変数に()を忘れると展開がおかしくなるので注意
#define X(A) #A
変数(関数、クラス)名に展開
#define X(A) str##A
str(文字列)にAの変数(関数、クラス)名を連結させることができます。
#undef A
Aの定義を無効にする。#undefの行で無効になる
#ifdef A
(Aが定義されていたら行う宣言or処理)
#else
(Aが定義されていなかったら行う宣言or処理)
#endif
Aが#defineされていたら分岐を行う
#ifndef A
(Aが定義されていなかったら行う宣言or処理)
#else
(Aが定義されていたら行う宣言or処理)
#endif
#ifdefの逆、ヘッダーの多重インクルード防止によく使う
#include ファイル名
でファイルの内容を展開
(実はプリプロセッサの処理なのでプログラム途中で書いてもいい、
さらに開くものがテキスト形式ならば拡張子はなんでもいいっぽい)
今回はプリプロセッサを使って遊んでみる。
列挙体を子クラス配列の添え字(連想配列的なもの)にして、
親クラスにアップキャスト(ポリモーフィズム)してみる
とりあえずファイルふたつ用意
//Fruits.txt
X(Apple)
X(Grape)
X(Banana)
//main.cpp
#include <iostream>
using namespace std;
#define X(A) A,
enum{
#include "Fruits.txt"
};
#undef X
#define X(A) #A,
const char *Fstr[]={
#include "Fruits.txt"
};
#undef X
// 関数
void Func_Apple(){cout << "This is Apple" << endl;};
void Func_Grape(){cout << "This is Grape" << endl;};
void Func_Banana(){cout << "This is Banana" << endl;};
#define X(A) Func_##A,
void (*func[])() = {
#include "Fruits.txt"
};
#undef X
class Fruits
{
public:
virtual void Out(){}
};
class CApple: public Fruits {public: virtual void Out(){cout << "An Apple" << endl;}};
class CGrape: public Fruits {public: virtual void Out(){cout << "An Grape" << endl;}};
class CBanana: public Fruits {public: virtual void Out(){cout << "An Banana" << endl;}};
template <class T>
Fruits *createFruits(){ return new T;}
#define X(A) createFruits<C##A>,
Fruits *(*create[])() = {
#include "Fruits.txt"
};
#undef X
int main() {
#define strgen1(x) "x"
#define strgen2(x) x
#define strgen3(x) #x
char *p, *str = "abc";
p = strgen1(str); /* p = "x" */
cout << p << endl;
p = strgen2(str); /* p = "abc" */
cout << p << endl;
p = strgen3(str); /* p = "str" */
cout << p << endl;
#define symadd(x, y) sym##x + sym##y
int sym1 = 3, sym2 = 5;
int i = symadd(1, 2); /* sym1 + sym2 */
cout << i << endl;
cout << Apple << endl;
cout << Grape << endl;
cout << Banana << endl;
cout << Fstr[Apple] << endl;
cout << Fstr[Grape] << endl;
cout << Fstr[Banana] << endl;
func[Apple]();
func[Grape]();
func[Banana]();
Fruits *fruits1 = create[Apple]();
Fruits *fruits2 = create[Grape]();
Fruits *fruits3 = create[Banana]();
fruits1->Out();
fruits2->Out();
fruits3->Out();
delete fruits1;
delete fruits2;
delete fruits3;
return 0;
}
#define X
#undef X
を繰り返してるのはXで定義を統一してるためだけ(Xに深い意味はない)
列挙体を添え字でわたせば、
そのクラスをアップキャストして使えるようにできる
使い道的にはごり押しの連想配列に近い
STLが分かる人は素直にmapを使いましょう
#vote((^ω^)ktkr[0],普通[0],。(`ω´#)。なんじゃこりゃああ[0])