C言語で書き溜めたコード片を再利用のためにcplaygroundというリポジトリにまとめているよ、というはなし。
概要
最近、C言語でコードを書く機会が増えており(カーネルランドのものを書くときや、VMを最適化するためには、やはりCがやりやすいなどの理由により)、よくつかうデータ構造やアルゴリズムを再利用のために一つのリポジトリにまとめておくと
そこから切り貼りすればいつでも簡単に使えるのでは、と考え、そういうリポジトリを創ることにした。
以下で公開している。
この記事では、cplaygroundに現時点で含まれているデータ構造とアルゴリズムについて解説する。
また、Cを書く上でのちょっとしたテクニックについてもかけたら良いかもなあと思っている。そのため、C言語の初学者から中級者には参考になる内容が含まれているかもしれない。
それでは、続きからどうぞ。
続きを読むファイルシステムを自作しています.
どうも,最近Blog書こうという気持ちは高まっていてネタ帳は増えてるけど書いてなかったんでいい加減書こうと思って書きます.
筑波大学の情報学群情報科学類では,3年次に主専攻実験というものがあり,これは情報科学類に存在する3つの専攻に所属し,各専攻が開設する実験を履修するというものです.
それで,履修する実験は決められたルールのもとで自由に選ぶことができることになっています.
そこで自分は,春学期は「カーネルハック」を履修しています(面白いことに,今年は自分1人だけが履修しています.)
そこの課題の一つに,ファイルシステムを実装するというものがあり,初めにFUSEを用いたファイルシステムの実装を行っています. 半分趣味みたいな形で実験ができることは本当によくて家でも楽しく実装をしています.
実装や設計については続きからどうぞ.
続きを読むD言語で開発した自作Lisp,ChickenClispをFFIに対応させた話
もともとこの記事は8月3日に書く予定だったようである.(僕はBlogの記事を書くときは,テキストエディタを用いて日付_記事の内容.mdとする 習慣がありそれによると2018-08-03_chickenclisp_ffi.mdとあったのでそういうことぽっぽい.) それで,今回の記事ではD言語で開発した自作LispであるChickenClispにFFI(Foreign Function Interface)を 使えるようにしたので,それについて書く.(なお,以下の内容は 情報科学特別講義A「Rubyインタプリタに見る実際のシステムソフトウェア」という授業のレポートで提出した内容に加筆・修正を加えたものである.)
また,前回の記事 C言語でヒープ領域にある機械語列に実行権限を与え,実行する方法. は今回の話でも出てくるので未読なら読んでおくと良いかもしれない.(最後の方にチョロっと出てきます)
それでは,本文は続きから.
続きを読むC言語でヒープ領域にある機械語列に実行権限を与え,実行する方法.
だいぶ前(3ヶ月)に書こうと思っていた記事なのだが,ぼけーっとしていたら夏休みも終わり,そして夏休みが終わってからも1ヶ月がたっており,いい加減記事をかくか,とおもい書くことにした.
とりあえず,本文は続きから
続きを読むD言語で,(ゴリゴリの)コンパイル時メタプログラミングでADTを実現した話
最近,OCamlでプログラミングすることが多く,久しぶりにDでプログラミングをすると,
やっぱりD言語にはどう考えても,ADT(代数的データ型)とパターンマッチが必要だと痛感した.
そこで,D言語でコンパイル時メタプログラミングを駆使し,ADTをD言語でもそれっぽく持ってくることに
成功した.
簡単に概要を書くと,
- OCamlに似たSyntaxのADT用の構文を定義する.
- それをコンパイル時にパースしASTを構築する.
- ASTを元にDのコードを生成する.
- 生成されたDのコードを文字列mixinすると,生成したADTを使うことができるようになる.
という感じである.とりあえず,リポジトリはこれ: https://github.com/alphaKAI/dadt
それでは,続きから具体的な話をしていく.
注: もともと2018/08/03に書く予定だったBlogを途中で書くのを中断していて,だいぶ日付が立ってしまった今日(2018/08/29)思い立って続きを書いたので最近やっていること(自作処理系の開発.これもBlogを書く予定)からはそれてしまうが,もったいないので書き上げて公開することにした.また,レポートに同じ内容を書いたので,そこから盛ってきている文章も多い.そのため,途中から文体が変わってしまっているかも知れない...
続きを読むD言語でもboost::anyをする.(任意の型の値を入れられるコンテナをつくる)
きっかけ
Twitterかどこかで,Type Erasureなる文字をみた.
そういえば,以前JavaのGenericsの実装がType Erasureを用いてるみたいなことを調べたことがあったけど(あれはList
そうすると,次の記事がでてきた猿でも分かった~型消去技法とは - 418 I'm a teapot
なるほど,要するにダックタイプができるってことっぽい.
もう少し詳しく書くと,2つのクラスに継承関係がなくても,例えば共通の関数fを実装しているということがわかれば,それらを中身の差異はあれど,fという点においては共通であるから次のようなことができる.
import std.stdio; /* AとBは独立の型であって,継承関係はない. */ class A { void f() { writeln("A#f is called!"); } } class B { void f() { writeln("B#f is called!"); } } // A, Bはともに,void f();という共通のシグネチャをもっているから,void f()だけをもつプレースホルダーをつくる. abstract class Holder { abstract void f(); } // 実際に値を格納するコンテナをつくる. class Holder_Container(T) : Holder { T obj; this (T obj) { this.obj = obj; } override void f() { obj.f(); } } void main() { Holder[] objs; // Holder_ContainerはHolderを継承しているからHolderの配列に追加することが可能 objs ~= new Holder_Container!(A)(new A); // これは末尾に追加する演算 objs ~= new Holder_Container!(B)(new B); foreach (obj; objs) { obj.f; // それぞれ,A.fとB.fを呼び出している. } }
C++だとvirtual
キーワードを用いていたけど,Dではvirtual
はないので,abstract
にする.(こうすると,プレースホルダにもなる.)
こうすると,HolderというAとBをつなぐインターフェースのようなものを介して扱うことができて,静的な型がついていてもダックタイプができる.
で,書くまでもないけど,継承関係がない場合を上に書いたけど,ダックタイプでもなんでもない普通の継承関係がある場合は次のようにかけて,当然ちゃんと動く.(明示的にインターフェースを書く)
import std.stdio; interface HasF { void f(); } class A : HasF { void f() { writeln("A#f is called!"); } } class B : HasF { void f() { writeln("B#f is called!"); } } void main() { HasF[] hf; hf ~= new A; hf ~= new B; foreach (e; hf) { e.f; } }
これは基本なので説明するまでもない.
実装する.
とりあえず,先程の記事を読み進めると,boost::anyというものがあって,これは任意の型(実際は制約があるっぽい?けど)を入れることのできる型で,これも型消去を用いて実装されているらしい.
なるほど.これはDで実装するしかないよね,となった.
D言語には似たようなものとしてstd.variantにVariantがあるけど,あれは格納したい型の最大のサイズを計算して,その大きさのunionを持つということで値を保持している. 今回作るものはそういうものではなくて,型は静的に解決されている.
とりあえず,boost::anyのコードをみるのもいいけど,実際にC++で実装している記事を見たので,それをDに持ってくることにした.
boost::anyを実装してみる - (void*)Pないと
で,実装した.
import std.traits, std.meta; class Any { private { /* インターフェース */ abstract class _AnyBase { abstract TypeInfo type(); abstract _AnyBase clone(); } /* 実際に値を保持するクラス */ class _Any(T) : _AnyBase { T value; this (T value) { this.value = value; } override TypeInfo type() const { return typeid(T); } override _AnyBase clone() { return new _Any!T(this.value); } ~this () {} } /* 内部で値を保持する(こいつがミソ) (ある型の値を持っているのに型情報が型に含まれてないのがポイント!) */ _AnyBase obj; } public { /* コンストラクタ - 引数が無いもの - 一般の値を引数にできるもの - Any型が引数のもの の3種類. */ this () {} this(T)(T value) { this.obj = new _Any!T(value); } this(Any any_obj) { if (any_obj.obj !is null) { this.obj = any_obj.obj.clone(); } else { this.obj = null; } } // TがAnyとは無関係であるということを言わないと,opAssignは定義できないので Any opAssign(T)(T value) if (!is(T == Any)) { delete this.obj; this.obj = new _Any!T(value); return this; } // この関数を用いて値を取り出す. T castTo(T)() { if (this.obj is null) { throw new Exception("this object has no value"); } if ((cast(_Any!T)this.obj) is null) { throw new Exception("can not cast into incompatible type"); } else { return (cast(_Any!T)this.obj).value; } } // 型情報を返す. TypeInfo type() { return this.obj.type; } } } import std.stdio; class K { int value; this (int value) { this.value = value; } } void main() { Any a = new Any; a = 10; writeln(a.type); writeln(a.castTo!int); a = [1, 2, 3, 4, 5]; writeln(a.type); writeln(a.castTo!(int[])); a = new K(123); writeln(a.type); writeln(a.castTo!(K).value); }
えーと,結論からいうと これがなんでできているかというと,
内部にはまず,2つのクラスがある._AnyBase
型と_Any!T
型である.
そして,_Any!T
は_AnyBase
を継承している.ここで型変数T
が消えていることに注目.
つまり,実際に値を保持するobj
は_AnyBase型であり,型情報(型変数)をもたない!
つまり,どんな型T
がきて_Any!T
がつくられても,_AnyBase
はそれを受け入れることができるから,どんな型でも保持できるってワケ
まとめると
- 実際に値を保持するのは
_AnyBase obj
で,これは内部ではobj = new _Any!T(T型の値)
というように使われる(_Any!T
は_AnyBase
を継承しているので,_AnyBase
に入れることができる) _AnyBase
は中身(値であったりその型)には言及していない.つまり,どんな_Any!T
もうけとれる ← ここがミソ
まとめ
すごく久しぶりにブログを書いたので,ブログの書き方を忘れてしまったので,すごい適当な記事になってしまった.