こんなのがコンパイラを通るんだね

コンパイラインタプリタの差がよく分からない。

だとして、例えばソースを一度全部眺めて関数定義を全部メモしておくけど、コードの実行は一行ずつ読む。なんて場合は何というのだろうか?
C言語は呼び出される関数が呼び出しよりも先に定義(宣言でもいいが)されてないと動かないように、インタプリタもまだ読んでいない部分に関数が宣言されていたとしても、読むまでは分からない。これじゃ不便なので関数定義だけは先にざっと探してメモしておく俺言語を作ってみたいのだが、これはコンパイラなんだろか。

//ActionScript風インタプリタだと思って下さい。
var num:int 10;

//上から一行ずつ読んでいる場合
//この時点ではまだ関数add10が何者か分からない。
num = add10(num);//エラー。そんな関数ないよ!
function add10(n:int):int{
	return n + 10;
}

まぁそんなことはどうでも良い。俺様言語VirtualMachineとしてしまえばどっちでも通りそうだからこの話はキニシナイでおこう。
当たり前なのかもしれないけど、今日、こんなコードがAS3で通る事を知りました。

var num:int = 0;
var abc:int = 0;
abc = 1 + (num = 1 + 2);

//abc = 1 + num = 1 + 2;//ちなみにこっちは通らない。

trace("num:" + num + " abc:" + abc);//num:3 abc:4

//なんもしない式たち。でもコンパイラは通る。
10;
num;
(1);
(abc);

C言語でも通る。()の中身は別の式として処理しているからなんだろか。
確かに自分でスクリプト言語作っていくとこれが問題ない式だと思えるところが何とも不思議だけど、
言語処理の勉強でこういうところに気がついて行けるのはとても楽しいね。

気が付いたら1ヶ月も経っていやがる。

お勉強中。
とりあえず

という順序で勉強するべきなのだろうと思います。

AS3のArrayクラスは配列としてもQueueとしてもStack的にも使えるので便利だと思うのですが、あえて言うならpeekが欲しい。

var stack:Array = new Array();

//こうするの面倒くさい。
stack[stack.length - 1];

//これが欲しい
stack.peek();

拡張すんのなんか嫌だなぁ。
なんてクラスにするのさ。

本格的に作るのは難しい

本格的にスクリプトエンジンを組もうとすると再帰下降構文解析やらEBNFやらを覚える必要があるみたい。
ネガティブな話だけどいきなりそういうのは無理っぽいので、もっと単純に考えようと思う。
簡単でグラフィカルに動けばいいという方針で。

C++ 配列 長さ 取得

これでググっても案外出てこない。
C++でこんな風に書きたいと思ってるんだろうけど不思議なもんで出てこない。

int[] ary = new int[10];
system.out.println(ary.length);//10

あいかわらずmelponさんのパクりだけど書いてみよう。

#include <iostream>

//配列クラス
//ary[index]で要素アクセス出来て
//ary.lengthで長さが取得できればいい
template<class T>
class Array{
private:
	T*		m_pAry;
public:
	const int length;
	Array(int len):length(len),m_pAry(new T[len]){
		
	}

	~Array(void){
		if(m_pAry)delete[] m_pAry;
	}

	T& operator[](int idx){
		return m_pAry[idx];
	}
};


int main(void){
	//======================================
	//静的確保
	Array<int> ar(7);
	ar[0] = 5;
	
	//要素アクセスと長さの取得
	std::cout << ar[0] << std::endl;
	std::cout << ar.length << std::endl;

	//======================================
	//動的確保
	Array<int>* ar1 = new Array<int>(10);
	(*ar1)[0] = 5;
	
	//要素アクセスと長さの取得
	std::cout << (*ar1)[0] << std::endl;
	std::cout << (*ar1).length << std::endl;

	//======================================
	//char*だってはいるよー
	Array<char*>* ar2 = new Array<char*>(3);
	(*ar2)[0] = "test";
	
	//要素アクセスと長さの取得
	std::cout << (*ar2)[0] << std::endl;
	std::cout << (*ar2).length << std::endl;

	std::cout << "hello" << std::endl;
	char c;
	std::cin >> c;
	return 0;
};

動的な時が(*ar)[idx]ってなるから格好悪いね。

Luaさわってみた。

ただ計算するだけ。ここから
http://luabinaries.luaforge.net/download.html
lua5_1_4_Win32_dll8_lib.zip を解凍して適当なフォルダに置いてインクルードパスを通して、プロジェクトフォルダ内に以下二つを入れれば動く。

  • lua5.1.dll
  • lua51.dll

難しい使い方はまだ分からないけど、これだけでも外から読んできたテキスト動かしたりいい感じ。

//=============================================================================
// ExLua.h
//=============================================================================
#include <lua.hpp>
#pragma comment(lib,"lua51.lib")
//このコードはC++です
extern "C" {
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
};

//Luaで計算したいクラス
class ExLua{
private:
	lua_State *L;
public:
	ExLua(void){
		L = lua_open();
		luaL_openlibs(L);
	};
	~ExLua(void){
		lua_close(L);
		L = 0;
	};
	//計算する
	void exp(const char* str){
		luaL_dostring(L,str);
	}

	//値を返してもらう
	template<class T>
	T get(const char* str){
		lua_getglobal(L, str);
		T t = (T)lua_tonumber(L, -1);
		lua_pop(L,-1);//いちいちポップしないと落ちるわけではない。たまったらポップくらいでいいのかな。
		return t;
	}
};

//=============================================================================
// main.cpp
//=============================================================================
#include <iostream>
#include "ExLua.h"

int main(void){
	ExLua l;
	//計算式を突っ込む
	l.exp("a = 10");
	l.exp("b = 5");
	l.exp("c = a + b");
	l.exp("d = 7.7");

	//値を取り出す
	std::cout << l.get<int>("a") << std::endl;
	std::cout << l.get<int>("b") << std::endl;
	std::cout << l.get<int>("c") << std::endl;
	std::cout << l.get<double>("d") << std::endl;
	std::cout << l.get<int>("d") << std::endl;

	std::cout << "hello" << std::endl;
	char c;
	std::cin >> c;
}

C++クラスの書き方が面倒くさい訳

Cが全然分からないころの話。
javaのように1クラス1ファイルに書かないのはなんで?
メンバがポインタばっかりなのはなんで?
という疑問を持ちながらコーディングしてるともやもやして嫌でした。
もやもやがいやなのでjava風に書いていたら当然のように詰まりました。


ポインタじゃなかったらメモリが大変なことになるだろうが!
というのは最もなんですけど、じゃ小さいプログラムならありなのか?スマートポインタ使えばいいのか?と言うとそうでもない。
今日書いてみたいのは、クラスは相互参照がめんどくせぇから分割しろ。そして同じ理由でメンバ変数はポインタにしとこうぜ、というお話。

相互参照の解決

AクラスがBクラスを使って、BクラスがAクラスを使う。
こういう状態を相互参照とか相互includeとか言うと思う。
javaは何でもないことだけど、C++はこれを簡単には許してくれない。


相互参照の解決方法には前方宣言がある。
具体的にはこういうのが通らない。

//Foo.h============================
#include "Bar.h"
class Foo{
	Bar* bar;
};

//Bar.h============================
#include "Foo.h"
class Bar{
	Foo* foo;
};
//main.cpp============================
#include "Foo.h"//FooでもBarでもいい
int main(void){
	return 0;
}

通らない理由は両ヘッダが相互にincludeしているからだ。
includeは指定したテキストをそこに書いているだけのアナログな処理だ。
下のコードの場合はBarクラスがエラーになる。読み込み順序が逆ならFooがエラーになる。

#include "Bar.h"
class Foo{
	Bar* bar;
};

//↑↑↑↑↑↑↑↑↑↑
//上は下と同じこと
//↓↓↓↓↓↓↓↓↓↓

class Bar{
	Foo* foo;//この時点でBarは"定義"されておらず"宣言"すらされていないのでNG
};//Barの"定義"完了
class Foo{
	Bar* bar;//この時点でBarは"定義"されているのでOK
};//Fooの"定義"完了


どちらのクラスも相手が先に"宣言"済みでないとコンパイルできないがそれは無理な話。
これを解決するために前方"宣言"がある。

前方宣言する

具体的にはこうする必要がある

//Foo.h============================
//#include "Bar.h"
class Bar;//includeしないで前方"宣言"
class Foo{
	Bar* bar;
};

//Bar.h============================
//#include "Foo.h"
class Foo;//includeしないで前方"宣言"
class Bar{
	Foo* foo;
};

これだと問題なくコンパイルできる。
気をつけるのは
・includeではなく前方宣言すること
クラスをインスタンス化するためには"定義"が必要だが、しないならそういう名前のクラスが存在することが分かればいい。
そのためにとりあえず"宣言"だけするのが前方"宣言"。

ポインタじゃないと

//Foo.h============================
class Bar;
class Foo{
	Bar bar;//←ポインタ型でない
};

//Bar.h============================
class Foo;
class Bar{
	Foo foo;//←ポインタ型でない
};

これが通らない。
通らない理由は"宣言"だけではインスタンス化できないからだ。
通したければヘッダを読み込む必要が出てくるのだが、相互参照している場合はそれができない。


だからメンバ変数はポインタがいいんじゃないかな、と。
実装と定義を分ける理由も結局は相互参照が面倒くさすぎるからかなーと思います。

//これは通らない
//Foo.h============================
class Bar;
class Foo{
	Bar* bar;
public:
	void baz(void){
		bar->fiz();//宣言だけではどんな関数があるかは分からないし……
	}
};
//Bar.h============================
class Foo;
class Bar{
	Foo* foo;
public:
	void fiz(void){
		foo->baz();//宣言だけではどんな関数があるかは分からないし……
	}
};



//これは通る
//Foo.h============================
class Bar;
class Foo{
	Bar* bar;
public:
	void baz(void);
};
//Bar.h============================
class Foo;
class Bar{
	Foo* foo;
public:
	void fiz(void);
};

//両方とも定義終了!ここから実装
//Foo.cpp============================
#include "Foo.h"
#include "Bar.h"
void Foo::baz(void){
	bar->fiz();
}
//Bar.cpp============================
#include "Bar.h"
#include "Foo.h"
void Bar::fiz(void){
	foo->baz();
}


javaはそんなの関係ないから楽ですね。