GtkD による GUI アプリケーションの開発 (Windows)

GtkD は GTK+ のD言語へのバインディングで、クロスプラットフォームに対応しています。以下では、Windows 環境で GtkD を使ってGUIアプリケーションを作成する手順について説明します。


ダウンロード

まず、以下の3つのファイルをダウンロードします。( ) 内のファイルは現時点での最新版です。

1. DMD (dmd.2.063.2.zip)

解凍してDドライブの直下に置きます。 → D:\dmd2

2. GtkD (GtkD-2.3.2.zip)

解凍して名前を GtkD に変更して、Dドライブの直下に置きます。 → D:\GtkD

3. GTK+ → Windows 32bit の all-in-one bundle (gtk+-bundle_3.6.4-20130921_win32.zip)

解凍して名前を gtk+ に変更して、Dドライブの直下に置きます。 → D:\gtk+

注) all-in-one bundle にはプログラムの実行に必要な dll が含まれています。

注) 64bit版はパソコンによってはうまく動作しません。(私の64bitパソコンでは動作しませんでした)

フォルダの配置はあくまで一例ですので、配置に合わせて以下の設定をして下さい。


gtkd.lib のビルド

rdmd2.bat を Build.d のあるフォルダ(D:\GtkD)に入れて実行すると gtkd.lib (スタティックリンクライブラリ)がビルドされます。このファイルは .exe ファイルをビルドする際に必要になります。尚、バッチファイルの名前を rdmd.bat にすると暴走するので注意して下さい。

rdmd2.bat

set path=D:\dmd2\windows\bin
rdmd Build.d

注)GtkD のバージョンによってはビルド出来ないことがありますので、その場合は別のバージョンで試してみて下さい。GtkD-2.3.2 では問題なくビルド出来ました。 → GtkD ダウンロードパッケージ一覧


sc.ini の設定

D:\dmd2\windows\bin\sc.ini の DFLAGS の行の末尾に “-ID:\GtkD\src” を追加します。 -ID の D はDドライブの意味です。GtkD の配置に合わせて設定して下さい。

sc.ini

[Version]
version=7.51 Build 020

[Environment]
LIB="%@P%\..\lib";\dm\lib
DFLAGS="-I%@P%\..\..\src\phobos" "-I%@P%\..\..\src\druntime\import" "-ID:\GtkD\src"
LINKCMD=%@P%\link.exe
LINKCMD64=%VCINSTALLDIR%bin\amd64\link.exe
VCINSTALLDIR=%VCINSTALLDIR%
WindowsSdkDir=%WindowsSdkDir%


GtkD プログラムの作成

sample.d は、“Hello!” を表示する GtkD プログラムです。

sample.d

import gtk.Label, gtk.Main, gtk.MainWindow;

void main(string[] args) {
  Main.init(args);
  MainWindow mainWindow = new MainWindow("^o^");
  mainWindow.setDefaultSize(200, 100);
  mainWindow.add(new Label("Hello!"));
  mainWindow.showAll();
  Main.run();
}


.exe ファイルのビルド

dmd2.bat を sample.d のあるフォルダに入れて実行すると gtkd.lib がスタティックリンクされて sample.exe がビルドされます。尚、バッチファイルの名前を dmd.bat にすると暴走するので注意して下さい。

dmd2.bat

set path=D:\dmd2\windows\bin
dmd -L+D:\GtkD\gtkd.lib sample.d -ofsample.exe


実行

run.bat を sample.exe のあるフォルダに入れて実行するとプログラムが起動します。

run.bat

set path=D:\gtk+\bin
sample.exe

尚、起動時にDOS窓を表示させたくない場合は、run.vbs を run.bat と同じフォルダに置き、run.vbs から実行するとDOS窓は表示されません。

run.vbs

CreateObject("WScript.Shell").Run "run.bat",0


参考資料
1.GtkD Tutorial
2.gtkd-developers/GtkD – Installing on Windows

広告

多次元配列と1次元配列の対応関係

多次元配列と1次元配列の対応関係をまとめておきます。

多次元配列と1次元配列の対応関係

2次元配列 <-> 1次元配列
double[n1][n2] <-> double[n1 * n2]
a2[i1][i2] <-> a1[i1 * n2 + i2]

3次元配列 <-> 1次元配列
double[n1][n2][n3] <-> double[n1 * n2 * n3]
a3[i1][i2][i3] <-> a1[i1 * n2 * n3 + i2 * n3 + i3]

4次元配列 <-> 1次元配列
double[n1][n2][n3][n4] <-> double[n1 * n2 * n3 * n4]
a4[i1][i2][i3][i4] <-> a1[i1 * n2 * n3 * n4 + i2 * n3 * n4 + i3 * n4 + i4]

5次元配列 <-> 1次元配列
double[n1][n2][n3][n4][n5] <-> double[n1 * n2 * n3 * n4 * n5]
a5[i1][i2][i3][i4][i5] <-> a1[i1 * n2 * n3 * n4 * n5 + i2 * n3 * n4 * n5 + i3 * n4 * n5 + i4 * n5 + i5]

converter.cpp
// 1-dimensional array -> 2-dimensional array
void convert(double* a1, double** a2, int n1, int n2) {
  int i = 0;
  for (int i1 = 0; i1 < n1; ++i1) {
    for (int i2 = 0; i2 < n2; ++i2) {
      a2[i1][i2] = a1[i++];
    }
  }
}

// 2-dimensional array -> 1-dimensional array
void convert(double** a2, double* a1, int n1, int n2) {
  int i = 0;
  for (int i1 = 0; i1 < n1; ++i1) {
    for (int i2 = 0; i2 < n2; ++i2) {
      a1[i++] = a2[i1][i2];
    }
  }
}

// 1-dimensional array -> 3-dimensional array
void convert(double* a1, double*** a3, int n1, int n2, int n3) {
  int i = 0;
  for (int i1 = 0; i1 < n1; ++i1) {
    for (int i2 = 0; i2 < n2; ++i2) {
      for (int i3 = 0; i3 < n3; ++i3) {
        a3[i1][i2][i3] = a1[i++];
      }
    }
  }
}

// 3-dimensional array -> 1-dimensional array
void convert(double*** a3, double* a1, int n1, int n2, int n3) {
  int i = 0;
  for (int i1 = 0; i1 < n1; ++i1) {
    for (int i2 = 0; i2 < n2; ++i2) {
      for (int i3 = 0; i3 < n3; ++i3) {
        a1[i++] = a3[i1][i2][i3];
      }
    }
  }
}

C++ で作成した DLL をD言語のプログラムから呼び出す手順

D言語のプログラム(.d ファイル)から DLL を呼び出すやり方には、暗黙的リンクと明示的リンクの二つのやり方があります。
既に、C/C++ で作成した .dll ファイルがあれば、DLL を呼び出す手順は次のようになります。

暗黙的リンクの場合
1. implib で .dll ファイルから .lib ファイルを生成する。
2. 暗黙的リンク用の .d ファイルを作成する。(.d ファイルの中でリンクする .dll のファイル名を明示しません)
3. dmd で .d ファイルと .lib ファイルから .exe ファイルをビルドする。
4. .exe ファイルと .dll ファイルを同じフォルダに入れて .exe ファイルを実行すると .dll ファイルが呼び出される。
明示的リンクの場合
1. 明示的リンク用の .d ファイルを作成する。(.d ファイルの中でリンクする .dll のファイル名を明示します)
2. dmd で .d ファイルから .exe ファイルをビルドする。
3. .exe ファイルと .dll ファイルを同じフォルダに入れて .exe ファイルを実行すると .dll ファイルが呼び出される。

詳細は、「clang++ による DLL (ダイナミックリンクライブラリ) のビルド」の「.lib ファイルのビルド」以降の記事をご覧ください。

尚、最初に .dll ファイルと .h ファイルがある場合は、htod で .h ファイルを .d ファイルに変換し、DLL をリンクする .d ファイルの中で import し、dmd でビルドします。例えば、array.dll と array.h がある場合は、まず、htod で array.h を array.d に変換します。

array.h

#ifndef ARRAY_H
#define ARRAY_H

void setArray(double*, int, double);
double getArray(double*, int);

#endif

h2d.bat

set path=D:\htod
htod array.h -o array.d

array.d

/* Converted to D from array.h by htod */
module array;
//C     #ifndef ARRAY_H
//C     #define ARRAY_H

//C     void setArray(double*, int, double);
extern (C):
void  setArray(double *, int , double );
//C     double getArray(double*, int);
double  getArray(double *, int );

//C     #endif

そして、implicit.d の中で array.d を import します。

implicit.d

import array;
import std.stdio;

void main() {
  uint n = 10;
  auto a = new double[](n);
  a[] = 0;
  foreach (i; 0..a.length) {
    setArray(a.ptr, i, cast(double)i);
    writefln("a[%d] = %.5f", i, getArray(a.ptr, i));
  }
}

implicit.d、array.d、array.lib を同じフォルダに入れて implicit.bat を実行すれば、caller.exe がビルドされます。

implicit.bat

set path=D:\dmd2\windows\bin
dmd -O -release implicit.d array.lib -ofcaller.exe

後は、caller.exe と array.dll を同じフォルダに入れて実行するだけです。


現状では D言語の double[] 型の1次元配列 a を C++ の double* 型引数に a.ptr で渡すことはできたのですが、double[][] 型の2次元配列を double** 型引数に渡すことはできませんでした。そのため、多次元配列を扱う場合は、一旦1次元配列に変換する必要がありそうです。
また、D言語の string 型データを C++ の stiring 型引数に渡すのもうまくいきませんでした。文字列に関しては、<iostream> を使って、単に文字列を標準出力するプログラムも実行時にエラーが発生するので、Clang 側の問題かもしれません。

結論:
暗黙的リンクは、同じ言語の関数を呼び出すのと同じような感覚で、異なる言語の関数を呼び出せるので、お薦めです!

clang++ による DLL (ダイナミックリンクライブラリ) のビルド

D言語で作成した数値計算プログラムが思っていたほど速くなかったので、C++ で作成した関数を clang++ で DLL にコンパイルし、それを D言語のプログラムから呼び出すことを検討しています。

以下では、clang++ を使って DLL をビルドした後、D言語のプログラムから DLL を呼び出す手順について説明します。


ダウンロードとインストール

1. LLVMClang for Windows (現時点での最新版:LLVM-3.4-win32.exe)

Dドライブの直下にインストールします。 → D:\LLVM

2. MinGWMinGW – Minimalist GNU for Windows (mingw-get-setup.exe)

Dドライブの直下に MinGW をインストールします。 → D:\MinGW

MinGW Installation Manager を起動して、”mingw32-gcc-g++” を選択 → Mark for Installation


メニューから、Installation → Apply Changes


3. DMD (現時点での最新版:dmd.2.063.2.zip)

解凍して Dドライブの直下に置きます。 → D:\dmd2

4. Digital Mars C and C++ Compiler PackagesBasic Utilities (bup.zip)

解凍して Dドライブの直下に置きます。 → D:\dm

dm の中にある implib.exe は、.dll ファイルからインポートライブラリ (.lib ファイル) を生成する際に必要になります。

フォルダの配置はあくまで一例ですので、配置に合わせて以下の設定をして下さい。


DLL ビルド用プログラムの作成 (C++)

DLL をビルドするのに必要なファイルは、呼び出される関数が入ったファイル (array.cpp) だけです。モジュール定義ファイルはなくても構いません。

array.cpp

extern "C" void setArray(double* a, int i, double ai) {
  a[i] = ai;
}

extern "C" double getArray(double* a, int i) {
  return a[i];
}


.dll ファイルのビルド

dll.bat を array.cpp のあるフォルダに入れて実行すると array.dll がビルドされます。

dll.bat

set path=D:\LLVM\bin;D:\MinGW\bin
clang++ -std=c++11 -O -shared array.cpp -o array.dll


.lib ファイルの生成

lib.bat を array.dll のあるフォルダに入れて実行するとインポートライブラリ array.lib (テキストファイル)が生成されます。インポートライブラリ (.lib ファイル) は暗黙的リンクの場合に必要になります。明示的リンクの場合は必要ありません。

lib.bat

set path=D:\dm\bin
implib /system array.lib array.dll


DLL リンク用プログラムの作成 (D言語)

DLL をリンクするプログラムは、暗黙的リンクの場合と明示的リンクの場合で異なります。明示的リンクの場合は、リンクする .dll のファイル名を明示する必要がありますが、暗黙的リンクの場合は、その必要はありません。

(暗黙的リンクの場合)

implicit.d

import std.stdio;

extern (C) void setArray(double*, int, double);
extern (C) double getArray(double*, int);

void main() {
  uint n = 10;
  auto a = new double[](n);
  a[] = 0;
  foreach (i; 0..a.length) {
    setArray(a.ptr, i, cast(double)i);
    writefln("a[%d] = %.5f", i, getArray(a.ptr, i));
  }
}

(明示的リンクの場合)

explicit.d

import std.stdio;

extern (Windows) void* LoadLibraryA(in char*);
extern (Windows) void* GetProcAddress(void*, in char*);
extern (Windows) int FreeLibrary(void*);

alias extern (C) void function(double*, int, double) type_setArray;
alias extern (C) double function(double*, int) type_getArray;

void main() {
  auto dll = LoadLibraryA("array.dll");
  auto setArray = cast(type_setArray)GetProcAddress(dll, "setArray");
  auto getArray = cast(type_getArray)GetProcAddress(dll, "getArray");
  uint n = 10;
  auto a = new double[](n);
  a[] = 0;
  foreach (i; 0..a.length) {
    setArray(a.ptr, i, cast(double)i);
    writefln("a[%d] = %.5f", i, getArray(a.ptr, i));
  }
  FreeLibrary(dll);
}


.exe ファイルのビルド

暗黙的リンクの場合は、implicit.bat を implicit.d と array.lib のあるフォルダに入れて実行します。また、明示的リンクの場合は、explicit.bat を explicit.d のあるフォルダに入れて実行します。エラーがなければ、caller.exe がビルドされます。

(暗黙的リンクの場合)

implicit.bat

set path=D:\dmd2\windows\bin
dmd -O -release caller.d array_h.d array.lib -ofcaller.exe

(明示的リンクの場合)

explicit.bat

set path=D:\dmd2\windows\bin
dmd -O -release explicit.d -ofcaller.exe


実行

暗黙的リンク、明示的リンク、いずれの場合も run.bat を caller.exe と array.dll のあるフォルダに入れて実行すると array.dll が呼び出されます。実行時に .lib ファイルは必要ありません。尚、D:\MinGW\bin にパスを通しているのは、実行時に libgcc_s_dw2-1.dll が必要になるからです。

run.bat

set path=D:\MinGW\bin
caller.exe
pause

以上の説明では、分かり易くするために個別にバッチファイルを作成していますが、実際には一連の処理を1つのバッチファイルで行っています。

C++における多次元配列の動的確保

C++では、3次元配列の記憶領域を new 演算子を使って次のように確保することができません。

  double*** a = new double[10][20][30];

C++における多次元配列の動的確保には色々な方法がありますが、私は3次元配列をメンバーに持つクラス(下記の例では、Array3d)を作成し、そのインスタンスを動的に確保することで実現しています。
他の方法については、この記事の最後の参考サイトを御覧ください。


注)下記のやり方では、実行時に要素数を決定できないという弱点があります!実行時に要素数を決定したい場合は、他の方法を使ってください。


テンプレートを使わないやり方

array3d.hpp

#pragma once

class Array3d {
public:
  static const int length1 = 10;
  static const int length2 = 20;
  static const int length3 = 30;
  double _[length1][length2][length3];
  Array3d();
};

array3d.cpp

#include "array3d.hpp"

Array3d::Array3d() {
  for (int i = 0; i < length1; i++) {
    for (int j = 0; j < length2; j++) {
      for (int k = 0; k < length3; k++) {
        _[i][j][k] = 0;
      }
    }
  }
}

main.cpp

#include <iostream>
#include "array3d.hpp"

int main() {
  auto array3d = new Array3d();

  for (int i = 0; i < Array3d::length1; i++) {
    for (int j = 0; j < Array3d::length2; j++) {
      for (int k = 0; k < Array3d::length3; k++) {
        array3d->_[i][j][k] = 1;
        std::cout << array3d->_[i][j][k] << std::endl;
      }
    }
  }

  delete array3d;
}


テンプレートを使ったやり方

array3d.hpp

#pragma once

template <int LENGTH1, int LENGTH2, int LENGTH3> class Array3d {
public:
  const int length1 = LENGTH1;
  const int length2 = LENGTH2;
  const int length3 = LENGTH3;
  double _[LENGTH1][LENGTH2][LENGTH3];

  Array3d() {
    for (int i = 0; i < length1; i++) {
      for (int j = 0; j < length2; j++) {
        for (int k = 0; k < length3; k++) {
          _[i][j][k] = 0;
        }
      }
    }
  }
};

main.cpp

#include <iostream>
#include "array3d.hpp"

int main() {
  const int length1 = 10;
  const int length2 = 20;
  const int length3 = 30;

  auto array3d = new Array3d<length1, length2, length3>();

  for (int i = 0; i < array3d->length1; i++) {
    for (int j = 0; j < array3d->length2; j++) {
      for (int k = 0; k < array3d->length3; k++) {
        array3d->_[i][j][k] = 1;
        std::cout << array3d->_[i][j][k] << std::endl;
      }
    }
  }

  delete array3d;
}


参考サイト

1.[C++] new による多次元配列の動的作成
2.C++で二次元配列を動的生成する3つの方法
3.C++ メモリの動的確保(new)
4.C++のvectorで多次元配列を使うときのメモ

C vs C++ vs Java vs D

dmd に最適化オプション (-O -release) を付け忘れていたので、以前の記事を訂正しました。(2014.05.03)

C 、C++ 、Java 、D の処理速度を ガウスの消去法(ピボット選択なし) を用いて計測しました。尚、数値計算の過程で異常値が発生しないように、係数行列 aij は、要素の値がすべて 1 の上三角行列として設定しています。

 1.C++(clang++): 0.203sec (1.00)    187KB(exeファイル)
 1.C(clang) : 0.203sec (1.00) 93KB(exeファイル)
 2.C(gcc) : 0.344sec (1.69) 184KB(exeファイル)
 3.D(dmd) : 0.488sec (2.40) 459KB(exeファイル)
 4.Java : 0.785sec (3.87) 1.68KB(jarファイル)

C/C++ が一番速いのは予想通りでしたが、D が C/C++(clang/clang++) に比べ2倍以上遅いのは予想外でした。C/C++ と同レベルと思っていただけにちょっと残念な結果でした。
それから、clang と gcc の比較ですが、clang が一歩リードしています。これからは、clang の時代になりそうですね! 尚、どちらも最適化オプションを付けてコンパイルしています。gauss.c (clang) と gauss.c (gcc) のソースコードの違いは、const int n = 1000; と #define N (1000) の違いだけです。また、gcc は、古いバージョンの MinGW の方が新しいバージョンよりも処理速度が速く(0.281sec)、バイナリのサイズも小さいです(50KB)。結果は、現時点で最新の MinGW を用いたものを記載しています。
C++ では vector を使用していますが、サイズが固定なので vector 使用による処理速度の低下はありません。vector は多次元配列を簡潔に記述できるのでお薦めです。尚、C++ の for 文だけ、前置インクリメントを使用しています。今回のように int 型のインクリメントの場合は、後置インクリメントの処理速度と変わりませんが、コンテナのイテレータのように前置インクリメントが後置インクリメントより処理速度が速くなることがあるため、C++ では前置インクリメントが推奨されているそうです。このブログでは混在しています^^;

追記)Swift版、Kotlin版、Dart版のプログラムを追加しました。バグがあればご指摘願います!(2016年6月1日)

注)C++ でコンパイルする時は、オプション -std=c++11 を付けて下さい。付けない場合は、入れ子になったテンプレート引数において vector<vector<double> > のように、>> の間に空白を入れる必要があります。

gauss.cpp (clang++)

#include <chrono>
#include <cstdio>
#include <vector>
using namespace std;

void solve(vector<vector<double>>& a, vector<double>& b) {
  int n = a.size();
  double s;
  for (int k = 0; k < n - 1; ++k) {
    for (int i = k + 1; i < n; ++i) {
      s = a[i][k] / a[k][k];
      for (int j = k + 1; j < n; ++j) {
        a[i][j] -= s * a[k][j];
      }
      b[i] -= s * b[k];
    }
  }
  for (int i = n - 1; i >= 0; --i) {
    for (int j = i + 1; j < n; ++j) {
      b[i] -= a[i][j] * b[j];
    }
    b[i] /= a[i][i];
  }
}

int main() {
  auto start = chrono::system_clock::now();
  int n = 1000;
  vector<vector<double>> a(n, vector<double>(n, 0));
  vector<double> b(n, 0);
/*
  a[0][0] = 1; a[0][1] = 1; a[0][2] = 1; a[0][3] = 1; a[0][4] = 1;
  a[1][0] = 0; a[1][1] = 1; a[1][2] = 1; a[1][3] = 1; a[1][4] = 1;
  a[2][0] = 0; a[2][1] = 0; a[2][2] = 1; a[2][3] = 1; a[2][4] = 1;
  a[3][0] = 0; a[3][1] = 0; a[3][2] = 0; a[3][3] = 1; a[3][4] = 1;
  a[4][0] = 0; a[4][1] = 0; a[4][2] = 0; a[4][3] = 0; a[4][4] = 1;
     b[0] = 1;    b[1] = 1;    b[2] = 1;    b[3] = 1;    b[4] = 1;
*/
  for (int i = 0; i < n; ++i) {
    for (int j = i; j < n; ++j) {
      a[i][j] = 1;
    }
    b[i] = 1;
  }
  solve(a, b);
/*
  for (int i = 0; i < b.size(); ++i) {
    printf("x[%d] = %.20E\n", i, b[i]);
  }
*/
  auto stop = chrono::system_clock::now();
  printf("%.6f%s\n", 0.000001 * chrono::duration_cast<chrono::microseconds>(stop - start).count(), "sec");
  return 0;
}

gauss.c (clang)

#include <stdio.h>
#include <sys/time.h>

const int n = 1000;
double a[n][n];
double b[n];

void solve(double a[n][n], double b[n]) {
  double s;
  for (int k = 0; k < n - 1; k++) {
    for (int i = k + 1; i < n; i++) {
      s = a[i][k] / a[k][k];
      for (int j = k + 1; j < n; j++) {
        a[i][j] -= s * a[k][j];
      }
      b[i] -= s * b[k];
    }
  }
  for (int i = n - 1; i >= 0; i--) {
    for (int j = i + 1; j < n; j++) {
      b[i] -= a[i][j] * b[j];
    }
    b[i] /= a[i][i];
  }
}

int main(void) {
  struct timeval start;
  gettimeofday(&start, NULL);
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      a[i][j] = 0;
    }
    b[i] = 0;
  }
  for (int i = 0; i < n; i++) {
    for (int j = i; j < n; j++) {
      a[i][j] = 1;
    }
    b[i] = 1;
  }
  solve(a, b);
/*
  for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++) {
    printf("x[%d] = %.20E\n", i, b[i]);
  }
*/
  struct timeval stop;
  gettimeofday(&stop, NULL);
  printf("%.6f%s\n", (stop.tv_sec - start.tv_sec) + 0.000001 * (stop.tv_usec - start.tv_usec), "sec");
  return 0;
}

gauss.c (gcc)

#include <stdio.h>
#include <sys/time.h>

#define N (1000)
double a[N][N];
double b[N];

void solve(double a[N][N], double b[N]) {
  double s;
  for (int k = 0; k < N - 1; k++) {
    for (int i = k + 1; i < N; i++) {
      s = a[i][k] / a[k][k];
      for (int j = k + 1; j < N; j++) {
        a[i][j] -= s * a[k][j];
      }
      b[i] -= s * b[k];
    }
  }
  for (int i = N - 1; i >= 0; i--) {
    for (int j = i + 1; j < N; j++) {
      b[i] -= a[i][j] * b[j];
    }
    b[i] /= a[i][i];
  }
}

int main(void) {
  struct timeval start;
  gettimeofday(&start, NULL);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      a[i][j] = 0;
    }
    b[i] = 0;
  }
  for (int i = 0; i < N; i++) {
    for (int j = i; j < N; j++) {
      a[i][j] = 1;
    }
    b[i] = 1;
  }
  solve(a, b);
/*
  for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++) {
    printf("x[%d] = %.20E\n", i, b[i]);
  }
*/
  struct timeval stop;
  gettimeofday(&stop, NULL);
  printf("%.6f%s\n", (stop.tv_sec - start.tv_sec) + 0.000001 * (stop.tv_usec - start.tv_usec), "sec");
  return 0;
}

gauss.d

import std.datetime, std.stdio;

class Gauss {
  int n;
  double[][] a;
  double[] b;

  this(double[][] a, double[] b) {
    n = a.length;
    this.a = a;
    this.b = b;
  }

  void solve() {
    double s;
    for (int k = 0; k < n - 1; k++) {
      for (int i = k + 1; i < n; i++) {
        s = a[i][k] / a[k][k];
        for (int j = k + 1; j < n; j++) {
          a[i][j] -= s * a[k][j];
        }
        b[i] -= s * b[k];
      }
    }
    for (int i = n - 1; i >= 0; i--) {
      for (int j = i + 1; j < n; j++) {
        b[i] -= a[i][j] * b[j];
      }
      b[i] /= a[i][i];
    }
  }
}

void main() {
  StopWatch stopWatch;
  stopWatch.start();
/*
  double[][] a = [[1, 1, 1, 1, 1], [0, 1, 1, 1, 1], [0, 0, 1, 1, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 1]];
  double[] b = [1, 1, 1, 1, 1];
*/
  int n = 1000;
  auto a = new double[][](n, n);
  auto b = new double[](n);
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      a[i][j] = 0;
    }
    b[i] = 0;
  }
  for (int i = 0; i < n; i++) {
    for (int j = i; j < n; j++) {
      a[i][j] = 1;
    }
    b[i] = 1;
  }
  auto gauss = new Gauss(a, b);
  gauss.solve();
/*
  foreach (i; 0..b.length) {
    writef("x[%d] = %.20E\n", i, b[i]);
  }
*/
  stopWatch.stop();
  writef("%.6f%s\n", 0.000001 * stopWatch.peek.usecs, "sec");
}

gauss.java

class Gauss {
  int n;
  double[][] a;
  double[] b;

  Gauss(double[][] a, double[] b) {
    n = a.length;
    this.a = a;
    this.b = b;
  }

  void solve() {
    double s;
    for (int k = 0; k < n - 1; k++) {
      for (int i = k + 1; i < n; i++) {
        s = a[i][k] / a[k][k];
        for (int j = k + 1; j < n; j++) {
          a[i][j] -= s * a[k][j];
        }
        b[i] -= s * b[k];
      }
    }
    for (int i = n - 1; i >= 0; i--) {
      for (int j = i + 1; j < n; j++) {
        b[i] -= a[i][j] * b[j];
      }
      b[i] /= a[i][i];
    }
  }
}

class Main {
  public static void main(String... args) {
    long start = System.nanoTime();
/*
    double[][] a = {{1, 1, 1, 1, 1}, {0, 1, 1, 1, 1}, {0, 0, 1, 1, 1}, {0, 0, 0, 1, 1}, {0, 0, 0, 0, 1}};
    double[] b = {1, 1, 1, 1, 1};
*/
    int n = 1000;
    double[][] a = new double[n][n];
    double[] b = new double[n];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        a[i][j] = 0;
      }
      b[i] = 0;
    }
    for (int i = 0; i < n; i++) {
      for (int j = i; j < n; j++) {
        a[i][j] = 1;
      }
      b[i] = 1;
    }
    Gauss gauss = new Gauss(a, b);
    gauss.solve();
/*
    for (int i = 0; i < b.length; i++) {
      System.out.printf("x[%d] = %.20E\n", i, b[i]);
    }
*/
    long stop = System.nanoTime();
    System.out.printf("%.6f%s\n", 0.000000001 * (stop - start), "sec");
  }
}

gauss.swift

let n = 1000

func solve(a:inout [[Double]], b:inout [Double]) {
  var s:Double
  for k in stride(from:0, to:n - 1, by:+1) {
    for i in stride(from:k + 1, to:n, by:+1) {
      s = a[i][k] / a[k][k]
      for j in stride(from:k + 1, to:n, by:+1) {
        a[i][j] -= s * a[k][j]
      }
      b[i] -= s * b[k]
    }
  }
  for i in stride(from:n - 1, through:0, by:-1) {
    for j in stride(from:i + 1, to:n, by:+1) {
      b[i] -= a[i][j] * b[j]
    }
    b[i] /= a[i][i]
  }
}

var a = [[Double]](repeating:[Double](repeating:0, count:n), count:n)
var b = [Double](repeating:0, count:n)

for i in 0..<n {
  for j in i..<n {
    a[i][j] = 1
  }
  b[i] = 1
}

solve(a:&a, b:&b)

for i in b {
  print(i)
}

gauss.kt

val n = 1000

fun solve(a:Array<Array<Double>>, b:Array<Double>) {
  var s:Double
  for (k in 0..n - 2) {
    for (i in k + 1..n - 1) {
      s = a[i][k] / a[k][k]
      for (j in k + 1..n - 1) {
        a[i][j] -= s * a[k][j]
      }
      b[i] -= s * b[k]
    }
  }
  for (i in n - 1 downTo 0) {
    for (j in i + 1..n - 1) {
      b[i] -= a[i][j] * b[j]
    }
    b[i] /= a[i][i]
  }
}

fun main(args:Array<String>) {
  var start = System.nanoTime()
  var a = Array(n, {Array<Double>(n, {0.0})})
  var b = Array<Double>(n, {0.0})
  for (i in 0..n - 1) {
    for (j in i..n - 1) {
      a[i][j] = 1.0
    }
    b[i] = 1.0
  }
  solve(a, b)
/*
  for (i in b) {
    println(i)
  }
*/
  var stop = System.nanoTime()
  System.out.printf("%.6f%s\n", 0.000000001 * (stop - start), "sec")
}

gauss.dart

class Gauss {
  int n;
  List<List<double>> a;
  List<double> b;

  Gauss(List<List<double>> a, List<double> b) {
    n = a.length;
    this.a = a;
    this.b = b;
  }

  solve() {
    double s;
    for (int k = 0; k < n - 1; k++) {
      for (int i = k + 1; i < n; i++) {
        s = a[i][k] / a[k][k];
        for (int j = k + 1; j < n; j++) {
          a[i][j] -= s * a[k][j];
        }
        b[i] -= s * b[k];
      }
    }
    for (int i = n - 1; i >= 0; i--) {
      for (int j = i + 1; j < n; j++) {
        b[i] -= a[i][j] * b[j];
      }
      b[i] /= a[i][i];
    }
  }
}

main() {
  int n = 1000;
  List<List<double>> a = new List<List<double>>();
  List<double> b = new List<double>();
  for (int i = 0; i < n; i++) {
    a.add(new List<double>());
    for (int j = 0; j < n; j++) {
      a[i].add(0);
    }
    b.add(0);
  }
  for (int i = 0; i < n; i++) {
    for (int j = i; j < n; j++) {
      a[i][j] = 1;
    }
    b[i] = 1;
  }
  var gauss = new Gauss(a, b);
  gauss.solve();
  for (final i in b) {
    print(i);
  }
}

D言語による DLL (ダイナミックリンクライブラリ) のビルド

D言語でも簡単に複数の関数から .dll ファイルをビルドすることができます。ビルドした .dll ファイルは、C言語、D言語どちらのプログラムからでも呼び出すことができます。

まずは、呼び出される側のプログラムです。dll.d、callee.d、callee.def の3つのファイルを用意します。

dll.d
import core.sys.windows.dll;
import std.c.windows.windows;

extern(Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) {
  switch (ulReason) {
    case DLL_PROCESS_ATTACH:
      dll_process_attach(hInstance, true);
      break;

    case DLL_PROCESS_DETACH:
      dll_process_detach(hInstance, true);
      break;

    case DLL_THREAD_ATTACH:
      dll_thread_attach(true, true);
      break;

    case DLL_THREAD_DETACH:
      dll_thread_detach(true, true);
      break;

    default:
      break;
  }
  return true;
}

callee.d
extern (C) int add(int i, int j) {
  return i + j;
}

extern (C) int multiply(int i, int j) {
  return i * j;
}

callee.def
LIBRARY callee.dll
EXETYPE NT
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
  add
  multiply

以上のファイルを使って、次のようにすると、callee.dll がビルドされます。

> dmd -shared dll.d callee.d callee.def -ofcallee.dll

続いて、呼び出す側のプログラムです。
まずは、C言語の場合です。

caller.c
#include <stdio.h>
#include <windows.h>

int main(void) {
  HINSTANCE dll = LoadLibrary("callee.dll");
  int (*add)(int, int) = (int (*)(int, int))GetProcAddress(dll, "add");
  int (*multiply)(int, int) = (int (*)(int, int))GetProcAddress(dll, "multiply");
  printf("%d\n", add(1, 1));
  printf("%d\n", multiply(1, 1));
  FreeLibrary(dll);
  return 0;
}
> gcc caller.c -o caller.exe

次に、D言語の場合です。

caller.d
import std.stdio;

extern (Windows) void* LoadLibraryA(in char*);
extern (Windows) void* GetProcAddress(void*, in char*);
extern (Windows) int FreeLibrary(void*);

alias extern (C) int function(int, int) typeOfFunction;

void main() {
  auto dll = LoadLibraryA("callee.dll");
  auto add = cast(typeOfFunction)GetProcAddress(dll, "add");
  auto multiply = cast(typeOfFunction)GetProcAddress(dll, "multiply");
  writeln(add(1, 1));
  writeln(multiply(1, 1));
  FreeLibrary(dll);
}
> dmd caller.d -ofcaller.exe

後は、.exe ファイルと同じフォルダに .dll ファイルを入れるか、.dll ファイルのある場所にパスを通しておけば実行できます。