Hatena::ブログ(Diary)

新言語 Xtalを作る日記

2010-01-25

バインダ改修中 09:03 バインダ改修中を含むブックマーク

結構何度も書き直してしまう関数のバインダ周りなんですが、また書き直してます。

これまでの記述方法でも書けるのですが、次のような新記法も追加することになりました。


XTAL_BIND(Vector){
  Xdef_method(normalize); // このような、もっと簡単な記述方法の追加
  Xdef_method(length);
}

また、引数の最後をArguments* または const ArgumentsPtr& とすることで、簡単に可変引数を受け取れるようになりました。

ボブヤマボブヤマ 2010/09/04 01:57 はじめまして。
CEDEC講演見てXTALに興味をもちました。
早速検証させて頂きます。
バインダ付きなスクリプトって素敵です。
応援しています、頑張ってください!!

itouh2itouh2 2012/09/14 12:29 すばらしくよくできている言語だと思います。
ありがとうございます

2010-01-21

標準ライブラリのバインド 12:35  標準ライブラリのバインドを含むブックマーク

たとえばluaでは、数学系の関数luaから使いたいという場合、

lua_State *L = lua_open();
luaopen_math(L);

というように、luaopen_math()を呼ぶ必要があります。これはsquirrelでもそうだったはずです。

使わないライブラリでメモリを圧迫しないようにできるわけですが、ちょっと面倒にも思います。

Xtalでは必要なときに自動的にバインドされる機構を入れました。

f: math::sin(1); // ここでmathのメンバがバインドされる

Xtalでは、使わないときはバインドされないし、使う際は何もC++側をいじることなく使うことができるわけです。


これは、ユーザーが作ったライブラリも同様です。


XTAL_PREBIND(UserClass){ // PREBINDでは、コンストラクタ、継承関係を記述
  it->inherit(cpp_class<UserBaseClass>()); // UserBaseClassを継承させる
  it->def_ctor(ctor<UserClass>()); // コンストラクタを登録
}

XTAL_BIND(UserClass){ // メンバを登録
  it->def_method("aaa", &UserClass::aaa);
  it->def_method("bbb", &UserClass::bbb)
  it->def_method("ccc", &UserClass::ccc)
  it->def_method("ddd", &UserClass::ddd)
  it->def_method("eee", &UserClass::eee)
  it->def_method("fff", &UserClass::fff)
}

と書いておけば、クラスのメソッドを呼び出すときなどに自動的にXTAL_BINDが実行され、メンバが登録されます。

XTAL_PREBINDは継承関係のチェックがなされる際、またはインスタンスの生成がされる際に呼び出されます。

2010-01-20

eval関数追加 23:56  eval関数追加を含むブックマーク

デバッガの実装のために「ブレークポイントにて停止中に式を評価する機能」が必要だったので実装していたのですが、ついでにbuiltin::eval関数として追加することにしました。

foo: 100;

eval("foo + 10").p; //=> 110;

また、永らく使用不可能となっていたixが、evalを使うことでシンプルに実装しなおすことができたので、復活することになりました。

2010-01-18

16byteアライメントを持つメンバを持つオブジェクトに対応 08:33  16byteアライメントを持つメンバを持つオブジェクトに対応を含むブックマーク

これまでxnew<T>で生成されたオブジェクトは16byteアライメントを持っていてもいなくても、関係なくmallocで取得したメモリに配置していました。

普通のnewも同様に、関係なく配置します。

なのでアライメントの問題はどうしようもないだろう、と思っていたのですが、

ゲームにおいては、16byteアライメントを持つことはよくありうる、といったこともあり、対応することにしました。

どうやって対応するか、この一週間悩んでやっと実装できました。


xnewで生成されたオブジェクトは、16byteアライメントを持つメンバがある場合16byteアライメントを保障します。

持たないオブジェクトに関してはこれまでどおりです。

#include <xmmintrin.h>

struct Vec128{
	__m128 a;
};

struct Spr{
	Vec128 v;
};

XTAL_BIND(Spr){
	it->def_var("v", &Spr::v);
}

void test(){
	Spr* p = new Spr();
	_mm_add_ps(p->v.a, p->v.a); // これは危険


	SmartPtr<Spr> s = xnew<Spr>();
	_mm_add_ps(s->v.a, s->v.a); // xnewで確保した場合は大丈夫
}

----

今回の実装の余波


AllocatorLibは次のようなインターフェイスと変更となりました。

class AllocatorLib{
public:
	virtual ~AllocatorLib(){}
	virtual void* malloc(std::size_t size);
	virtual void free(void* p, std::size_t size);

	virtual void* malloc_align(std::size_t size, std::size_t alignment);
	virtual void free_align(void* p, std::size_t size, std::size_t alignment);

	virtual void out_of_memory(){}
};

malloc_align, free_alignが増えています。

ただし、これらは実装しなくてもかまいません。デフォルトの実装でmallocを使って整列するメモリを返すようになっています。

memalignなど、もっと効率のよい関数がある場合にオーバーライドしてください。

--

また、RefCountingBase, Baseという基底クラスも修正しました。

仮想関数はすべてon_という接頭をつけた名前にリネームし、非仮想関数としました。

たとえばvirtual void rawmember();はvoid on_rawmember();となります。

デストラクタも非仮想とし、仮想関数を排除しました。

ただし、特別な仕組みを導入したため、それらは仮想関数と同様にオーバーライドできます。

2009-12-29

メモリアロケータ書いた 17:07  メモリアロケータ書いたを含むブックマーク


ええ、車輪の再発明だということは承知しておる!
Xtal全体がまぁ再発明ともいえるんですけど。

Xtalはユーザーが自由にmalloc, free関数を登録できるようになってるんですが、
「メモリ確保関数を用意して登録するのめんどくさい。単純にメモリのここからここまで使ってほしいんよ」ということもあると思うんです。
そのために、
・クラス一つで完結している
・メモリ領域を指定するだけ
グローバル変数などを使用しない
・まあいろいろととにかくシンプル
そんなライブラリを作ろうということで作りました。


チャンクの管理は赤黒木で行っています。
要求されたサイズにもっとも近く、またアドレス値が小さいチャンクを検索します。
管理領域のサイズは、フリーチャンクのときは16byte(というかポインタ4つ分)、使用チャンク時は8byte(というかポインタ二つ分)です。
赤黒木のノードとしての色と、使用中かどうかのフラグは、前チャンクを指すポインタに埋め込んでます。

性能評価して、まるでダメなら素直にdlmalloc組み込むか…。
しかし、メモリアロケータの性能評価ってどう計ったら効果的なんだろか?

以下ソース。

続きを読む

2009-12-28

Rubyバイナリデータを読み書きする 01:31  Rubyでバイナリデータを読み書きするを含むブックマーク


Rubyバイナリデータを読み書きするのには、pack, unpack使うじゃないですか。

僕もねぇ、Ruby使い始めてもう何年も経つんですけど、pack, unpackっていつまでたっても覚えられないし、
使うとなんか汚い煩雑なコードになっていくんですね。そんなことってないですか?


なんかライブラリないかなーとググりまして、こちらで紹介されているライブラリを一通り見てみました。
http://d.hatena.ne.jp/kenhys/20070522/1179848262
・binaryparser
・calibre-binaryreader
・bindata
・BitStructEx
しかし、なんかどれも複雑というか、いまいち直感的じゃないという気がします。


自分の理想の使い方の形は次のような感じです。

include(BinaryData)


# float3要素のベクトル構造体定義
Vector = struct{
  # Vectorの最初の要素はxで、float 32 bitの型という意味
  var x: F32

  # Vectorの二つ目の要素はyで、float 32 bitの型という意味
  var y: F32
  
  # まぁ!みて!このz要素の定義を!まるでJavaScriptの定義みたいだわ!
  var z: F32
}

# 3x3のマトリックス構造体定義
Matrix = struct{
  # Matrixの最初の要素はrowsで、ユーザーが定義したVector構造体の三つの要素を持つ配列という意味
  var rows: Vector[3]

  # ユーザーが定義した構造体をネストして、しかも配列として使えるということね!
}

#
Model = struct{
  # 頂点の数をinteger 32 bit型として持つ
  var vertex_count: I32

  # 上のvertex_countを長さとして使えるのね!
  var vertices: Vector[:vertex_count]

  var matrix: Matrix
}

# モデル構造体のインスタンス生成
model = Model.new

# 値を格納するのも、直感的な記法なのね!
model.vertex_count = 2;
model.vertices[0].x = 1
model.vertices[0].y = 2
model.vertices[0].z = 3
model.vertices[1].x = 4
model.vertices[1].y = 5
model.vertices[1].z = 6

model.matrix.rows[0].x = 10
model.matrix.rows[1].y = 20
model.matrix.rows[2].z = 30


sio = StringIO.new

# バイナリとして書き込む
Model.write_to_stream(sio, model)

#これでsioにはC言語で次のような感じで書いて出力したバイナリと同じフォーマットで入ってる
# struct Vector{ float x, y, z; };
# struct Matrix{ Vector rows[3]; };
# struct Model{ int vertex_count; Vector vertices[vertex_count]; Matrix matrix; };
# Model model = {...};
# fwrite(fp, &model, 1, sizeof(model));

# ストリームを一番最初に戻す
sio.pos = 0

# バイナリから読み込んで復元する
model_deserialized = Model.read_from_stream(sio)

# 文字列化して出力
print model_deserialized

文字列化されたプリント結果
{:vertex_count=>2,
 :vertices=>[{:x=>1.0, :y=>2.0, :z=>3.0}, {:x=>4.0, :y=>5.0, :z=>6.0}],
 :matrix=>
  {:rows=>
    [{:x=>10.0, :y=>0.0, :z=>0.0},
     {:x=>0.0, :y=>20.0, :z=>0.0},
     {:x=>0.0, :y=>0.0, :z=>30.0}]}}
で、そんな理想なライブラリの下地を作ってみました。

続きを読む

2009-12-26

文字列リテラルを渡されて生成されたStringオブジェクトは、メモリ確保が起こらないようにした 23:36  文字列リテラルを渡されて生成されたStringオブジェクトは、メモリ確保が起こらないようにしたを含むブックマーク

もともと文字列の長さがsizeof(int)-1のものは、即値になるようにしてメモリ確保が起きないよう工夫していましたが、

今回、文字列リテラルを渡されて生成されたStringオブジェクトも即値になるようにしました。



void foo(){
  StringPtr str = xnew<String>(XTAL_STRING("This is a pen."));
  // 変数strに直接文字列リテラルへのポインタが埋め込まれている

  AnyPtr a = XTAL_STRING("Hello, World");
  // 変数aに直接文字列リテラルへのポインタが埋め込まれている
}

2009-12-25

文字列リテラルのmurmurhashを即値にする 09:07  文字列リテラルのmurmurhashを即値にするを含むブックマーク

http://murmurhash.googlepages.com/

enum{
	hash_m = 0x5bd1e995,
	hash_seed = 0xdeadbeef,
	hash_r = 24
};

template <int N>
inline uint_t hashv(const char (&data)[N], uint_t h){
	uint_t k = data[0];
	k |= data[1] << 8;
	k |= data[2] << 16;
	k |= data[3] << 24;

	k *= hash_m; 
	k ^= k >> hash_r; 
	k *= hash_m;

	h *= hash_m;
	h ^= k;
	return hashv(reinterpret_cast<const char (&)[N-4]>(data[4]), h);
}

template<>
inline uint_t hashv<4>(const char (&data)[4], uint_t h){
	return (h ^ (data[2]<<16) ^ (data[1]<<8) ^ data[0])*hash_m;
}

template<>
inline uint_t hashv<3>(const char (&data)[3], uint_t h){
	return (h ^ (data[1]<<8) ^ data[0])*hash_m;
}

template<>
inline uint_t hashv<2>(const char (&data)[2], uint_t h){
	return (h ^ data[0])*hash_m;
}

template<>
inline uint_t hashv<1>(const char (&data)[1], uint_t h){
	return h;
}

template <int N>
inline uint_t hash(const char (&data)[N]){
	uint_t h = hashv(data, hash_seed ^ (N-1));
	h ^= h >> 13;
	h *= hash_m;
	h ^= h >> 15;
	return h;
}

int main(){
  uint_t n = hash("TEST");
  printf("%d", n);
}

VC EE 2008では即値になったようでした。

でも、コンパイラによっては、または長すぎる文字列だと即値にならないかも?

2009-12-23 Xidを効率よくしたい このエントリーを含むブックマーク

Xidというマクロは、簡単に書くと次のようになっています。

#define Xid(ident) intern(#ident, sizeof(#ident))


IDPtr intern(const char* str, int size){
  strのハッシュを計算して、インターン済み文字列が格納されているハッシュテーブルを引いて、
 存在したらそれを返し、存在しなければインターン済み文字列を生成して、
 ハッシュテーブルに格納して、それを返す。
}

これをもっと効率よく書けないかと、頭をしぼってます。

自分のC++の知識を総動員して考えたのですが、うまい案が思いつきません。

Xid(var)が0始まりの、ユニークな整数値を返すようなマクロが書ければいいんですけども。


extern vector<IDPtr> id_list;

#define Xid(ident) id_list[TO_INT(ident)]

と書けるように。

ほげほげ 2009/12/24 16:15 ユニークになるかどうかわかりませんが、
http://d.hatena.ne.jp/yupo5656/20040613/p1

xtalcoxtalco 2009/12/25 09:02 コメントありがとうございます。

ユニークにするのはちょっと無理そうですが、
ハッシュ値を即値にはできそうですね。

2009-12-22

1.0.1.1 09:04  1.0.1.1を含むブックマーク

uninitialize時に、StringSpaceが持っている配列の開放時に停止する不具合を修正しました。

ツイッター 09:07  ツイッターを含むブックマーク

作法とか勉強中。

Xtalのここが使いにくい!とか、更新が無いぞ!とか、なんでも気軽にツイッターで@dofixxつけてツイートしてください。