C#の2次元配列をC++に変換する

まず,MatrixIsomorphismの引数をMatからint[,]に変えておこう.⇒引数に int[,]が使えない.昨日見た記事では確かにできるようになっていたと思うのだが…

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/arrays

確かに,以下のような構文は認められている.

static void Print2DArray(int[,] arr)

ただし,これはC#の話だ.C++で可能かどうかは,また別の話になる.C++では最初の次元の要素数しか省略できない.ただし,下記のような呼び出しは可能なようだ.

int sum_all2(int size1, int size2, int arr[size1][size2])

ダメだ.コンパイルエラーになる.「C99だとこんな書き方ができるんだそうな」とあるのだが… 

https://qiita.com/Hiraku/items/babed27bc1d750c2e12d

C99というのは,1999年規格ということだから当然搭載されているものと思われるのだが… 以下の構文ならよいという記事があった.

static void sub_func( int (*a)[]

http://murakan.cocolog-nifty.com/blog/2011/08/cc-b731.html

確かにこの文は通る.ただし,アドレス計算を自前でやらなくてはならないというのが欠点だ.C++ではarrayとvectorが使えるはずだが,これらはどうなっているのだろう?arrayはサイズ固定の配列だが,vectorは可変長なのでこちらの方が使い易い可能性はある.どちらも多次元配列を作ることはできる.将来的には文字列を要素とする行列なども扱うようになる可能性があるので,arrayないしvectorを使うのが上策なのではないだろうか?処理速度ではarrayの方が速い.

配列サイズが変化するということはあまりなさそうだし,変化した場合にはそれなりの対処策もあるので,arrayを使うというのが現時点では妥当なのではないだろうか?なぜだろう.ElsieProject.hでデータ・タイプ arrayを宣言できない.同じブロックで関数の引数としてなら使えているのだが… 下記の構文なら一応通る.

int MatrixIsomorphism(int N, std::array<std::array<int, 12>, 12> m1);

しかし,

int MatrixIsomorphism(int N, std::array<std::array<int>> m1);

は通らない.以下は通った.

int MatrixIsomorphism(int N, std::vector<std::vector<int>> m1);

しかし,この形式ではC#で定義に使えない.c#にもVectorというのはあるが,これは「倍精度実数の対」というまったく別物だ.C#とC++をシームレスに繋ぐとしたら,今のところint[,]しかないのではないか?⇒C#の配列[,]はC++では読めない.やはり,マーシャリングが必要なようだ.ゼルコバの木ではこのようなときにどうしていたのだろう?ゼルコバの木には2次元配列というのはなかったかもしれない…

ELSIE の array クラスを廃止した

なぜだろう?主画面のウィンドウをドラッグ移動できない.最初に写像の検査を実行した後,フリーズ状態になっている.⇒入力待ちになっていただけだ.

Stringからchar*への変換がうまくゆかない.どうしても,Marshalingという操作が必要なようだ.ゼルコバの木のコードを見ると,この辺りではncopyNameという自前の関数を作って処理している.この関数ではまず,UNICODEをS_JISに変換するということもやっている.コード変換は必ずしも必要ないのではないかと思われるが… やっと動くようになった.以下のコードでString→char*の変換を行う.

pin_ptr<const wchar_t> pinchars1 = PtrToStringChars(file1);

内部で使っているISOMORPHなどへのアクセスを回避するため,NATIVEFUNC_API int ELSIENATIVE::MatrixIsomorphism(int N, Mat& m1, Mat& m2)のような関数を使って呼び出すようにした.一応何とかこなせたのではないかと思う.ただし,エラーが出ている.

MatrixComparisonを実行して,EchoingMethodで-1026エラーが発生している.FILENOTFOUNDだ!エンコードの問題とも考えれるが,文字列にエスケープ文字が入っていることが障害要因になっている可能性もある.Replaceを試してみたがうまくゆかなかった.残っている文字列は”D”だけなので,エスケープの問題というより,エンコードの問題ではないかという気がする.WcharからSJISに変換してみよう.

どうもいろいろ厄介なところがある.arrayというテンプレートクラスにアクセスできない.これはおそらく,ELSIEでarrayというクラスが定義されているためではないかと思われる.これはリネームしておくしかないのではないか?⇒対処した.これでncopyNameも動作するようになり,ファイルを読み込んで処理することができた.⇒これで標準のarrayクラスが自由に使えるようになった.

現行では,typedef int Mat[MAXN+1][MAXN+1];というのを使っているが,これも一般のarrayに置き換えておこう.ELSIENATIVEでは,ReadMatrixとMatrixIsomorphismがMatを使っている.⇒マトリックスはファイルから読み込んでいるので,最後まで読まないとマトリックスのサイズが判明しないが,読みながら行列に書き込んでいるので,あらかじめサイズを指定するか,ないし固定サイズの配列を用意するしかない.⇒MAXNを定数としてそれを超えるデータは読み捨てでよいのではないか?いや,そもそもMatは2次元配列だ.

(多分)サイズを指定しないと引数には使えないと思う.しばらくは,このままで運用することにしよう.いや,配列サイズが不明でも引数として渡すことができる.これができるのなら任意サイズの配列としておいた方がよい.配列は初期化されていなくてはならないから,その情報からサイズを割り出すことができるのだろう.

再起動時にコンソールのサイズと位置を復元

ともかく,コンソールが出せるようになった.おめでたいことだ.参照した【ワレコC#講座】には再起動時にウィンドウのサイズと位置を復元する方法も載っていたので実装しておこう.やり方は,いつも我々がやっているのと同じ,プロジェクトの設定に保存するという方法だ.

https://www.wareko.jp/blog/c-sharp-form-application-save-restore-window-state-position-size

主画面の保存・復元はできるようになったが,コンソールの設定の取得・設定が分からない.フォームのプロパティにはコンソールという項目は存在しない.コンソールを開くためにやったことは,C#プロジェクトのプロパティで出力の種類をコンソールアプリケーションに切り替えたというだけだ.⇒いや,簡単にConsole.WindowWidthなどのプロパティやメソッドにアクセスできるようだ,

一応取得・復元できるようになった.初期起動したとき,デフォルトサイズで表示された後→復元という動作になっているのが,気になるが,対策は難しい.Form1のコンストラクタの冒頭で更新するようにしても,そのときはすでにウィンドウは開いた状態になっている.まぁ,これは,ここまでとしておこう.

このツールのタイトルを行列同型検定とした.元々は群の同型検定を行うために開発されたものだが,ELSIEを発掘したことで守備範囲が拡大したと言ってよいだろう.対象は正方行列に限定とし,群の乗積表,グラフの隣接行列などの具象行列では台集合は行と列の並びと一致するものとする.ともかく,ELSIEから行列の同型検定論理を切り出してC#から呼び出せるようにしてみよう.

現行ではそれをやっているのは,MatrixIsomorphismで引数は,(int N, ISOMORPH *p0, Mat& d0, ISOMORPH *p1, Mat& d1)である.Nは行列のサイズ,MatはMAXN✕MAXNの整数行列,ISOMORPHは位相同型オブジェクトで作業領域と見られる.

コンソールが出せた!完璧だ!言うことなし

アプリケーションのプロパティでマルチスタートアップという指定ができる.これを使えばC++アプリとC#アプリを並列実行できそうだ.⇒ただし,C++の出力をDLLからEXEに切り替えるとまた,いろいろな不都合が噴出してくる.もし,これをやろうとするのなら EXE 生成用のプロジェクトを別途作って,それ以外の部分をDLL化するしかない.⇒さすがに,そこまでやるとなると右から左という訳にはゆかなくなってくる.ただし,そうすることのメリットもある.処理を切り分けてライブラリ化するというのはいずれやらなくてはならない仕事のうちだ.⇒とりあえず,ここのところはパスして先を急ぐことにしよう.

C++アプリを単独で起動することはできる.このコンストラクタからC#のフォームを開くことはできないだろうか?⇒かなり難しいと思う.エラーが発生するのは仕方ないので,エラーが発生してもアプリをアボートしないようにエラー処理に手を入れておくことにする.⇒現行バージョンのVSは型検査が厳密になっていて,const char* を char* と混同することができないようになっている.文字列をスローしている場合があるので,それを受けるときは const char* で取る必要がある.とりあえず,大体動作するようになった.

コンソールとフォームを同時に開くことができるようになった.コンソールプロジェクトを立てて,マルチスタートアップを仕掛けたというだけのことだが,この2つのウィンドウが同時に操作できたら,確かにかなりおもしろいと思うのだが… printfを実行しただけではコンソールには何もでない.やはり,このウィンドウのハンドルか何かを取得してそれを使って操作しないと何もできないような感じだ.⇒コンソールハンドルを取得することは可能なようだ.⇒しかし,ウィンドウハンドルをもらってもどうすることもできない.多分ウィンドウのデバイスコンテキストまでは取得できるとは思われるが,そのあとは細かい描画処理になってしまう.⇒std:coutを取り出して再利用できるようになった.

std::ostream &conout = std::cout;
conout << “Native::ElsieCommand #####################”;

これをグローバル変数に入れてやれば,多分マルチスレッドで使い回しできるはずだ.⇒かなり,難しいが,参照して出力できるようになった.⇒いや,やっぱり後が続かない.std:coutは使い回しできないような作りになっているようだ.あきらめるしかなさそうだ.以前やったように,テキストボックスに表示するというのが可能最大限ではないだろうか?ただし,テキストボックスにはC#からしか書き込みできない.コンソールアプリはイベントループを持っていないというのが致命的だ.

こんなのがあった.【ワレコC#講座】Windows FormアプリケーションでConsoleウインドウに文字を出力する https://www.wareko.jp/blog/output-text-string-to-console-window-with-windows-form-application-in-c-sharp

これで行けるんじゃないだろうか?

まずい.どこか壊してしまったようだ.ソリューションを開いたときにエラーが発生するようになってしまった.

image

WrapperClass.csというファイルは作っていないのではないかと思うのだが… クリーンビルドして再起動でなんとか収まった.

【ワレコC#講座】ではコンソールを使う2つの方法を紹介しているが,最初の方法はうまくゆかなかった.コンソールは開けたが,書き込みもクリアもできない.書き込みはデバッグコンソールに出力されてしまうだけでなく,Console.Clearで例外が発生してしまう.2番目の方法では成功した.C#プロジェクトのプロパティで出力の種類をWindowsアプリケーションからコンソールアプリケーションに切り替えるだけだが,この状態で実行するとWindowsフォームが通常通り開き,同時にコンソールも使えるようになる.Console.Writelineで問題なく出力できた.

コンソール出力は主にDLL側でやっているので,あまり使い道はないかもしれないが,この設計は悪くないと思う.Console.Clearもその通り実行できた.いや,これはかなりよいかもしれない.Console.WriteLineだけでなく,ラッパクラスのprintfも動作している.つまり,コンソールに出力されている.これこそ望んだところだ.Debug.WriteLineをすべてConsole.WriteLineに切り替えることができるだろうか?それができるとほとんどELSIEと準同型検定が一体化したというのに等しい.

!いや,見事に出てくれた!

image

完璧だ!言うことなし.このウィンドウをフォームに嵌め込めたらさらによいのだが,そこまで求めるのはやや過大な要求というべきだろう.これでこれまでの作業が全部そのまま活きてくることになる.かなりおもしろくなってきた!いや,さすがにそう簡単には動いてくれないかも知れない.ELSIEコマンドを受け付けるところまではゆくが,データファイル名の入力に失敗した後の動作が悪い.完全な動作するところまで仕込むことができるだろうか?かなり難しそうな気はする.

使い方の要領というのもありそうだが… コントロールがどこにあるのか分からなくなってしまうところが欠点だ.カーソルを置いても何も入力できなくなるというのは,フォーカスを失っているという意味だろう.どうも,使いこなすのはかなり難しそうだ.入力を受け付けるというのを止めて読み取り専用とすればおそらく問題はなくなるはずだが…ともかく,ELSIEと準同型の統合という作業を進めるしかないだろう.

土壷にハマって抜けられない

土壷にハマってしまった.完全に足が止まってしまっている.何とかしなくては.何をどうすればよいか?boostはすでに止めてある.あとは,C++ → CLI → C# を繋げればよいというだけなのだが… 実際,ゼルコバの木では昔からやっていることだ.何がどうまずいのか?確かにC#は今回始めてだが,基本的にはVB.NETとコンパチと考えられるからそれほど高い障壁になるとも思われない.わたしもいよいよヤキが回ったか?

C++で「重大度レベル    コード    説明    プロジェクト    ファイル    行    抑制状態 エラー    C2338    2 フェーズの名前参照は、C++/CLI または C++/CX ではサポートされていません。/Zc:twoPhase- を使用してください    ElsieComeBack    D:\準同型検定CS\ElsieComeBack\c1xx    1    」が出ている.

ようやく通った.ここまで来れば,あとはもうもらったようなものだ.⇒ようやく動いたと思ったのも束の間,またエラーが出てしまった.

▲ELSIEのコマンド2を実行して例外が発生した.

image

ELSIEはもともとコンソールアプリとして開発されていて,コンソール入出力なしでは実行はほとんど不可能だ.たとえば,ファイル名の入力を促すプロンプトが出て,ファイル名を受け取ったあと,処理の結果をダンプするなどの動作になっている.ファイル名が無効なら例外をスローするような作りになっているから,例外は当然のように発生する.フォームとコンソールを併用するか,ないしフォームでコンソールを代用するようなことはできないのだろうか?⇒不可能ではないかもしれない.コンソールアプリからフォームを開くことはできそうだ.

using System.Windows.Forms;

        [STAThread]
         static void Main()
         {
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);
             Application.Run(new Form1());
         }

プロジェクトのプロパティを変更し,

  • アプリケーションの種類(A)をコンソールアプリケーションにする
  • スタートアップオブジェクト(O)を Sub Main にする

ことで実現できる.これはやってみる価値はあるのではないだろうか?ELSIEの中身をすべてC#で書き換えるというのは現実的ではない.

まだCLI→DLLの接続はできていない

ELSIEにラッパクラスを追加するところまではできるのだが,そのクラスにメソッドを追加したところで,E0035が発生してしまう.重大度レベル    コード    説明    プロジェクト    ファイル    行    抑制状態 エラー (アクティブ)    E0035    #error ディレクティブ: “This library now requires a C++11 or later compiler – this message was generated as a result of BOOST_NO_CXX11_REF_QUALIFIERS being set”    ElsieProject    C:\Users\babalabo\vcpkg\installed\x86-windows\include \boost\multiprecision\detail\check_cpp11_config.hpp    34

メソッドをヘッダファイルではなく,CPPファイルに実装したら,エラーは解消した.ただし,C#でラッパクラスの名前空間を参照しようとすると,同様のエラーが発生してしまう.C#プロジェクト→参照の追加→ElsieProjectで namespace ElsieProjectにアクセスできるようになった.⇒いや,まだ通っていない.C#からラッパクラスの関数にアクセスできない.⇒どうも,やはりboostとの相性が悪いように思われる.boostを完全に停止することでようやく一通りのことができるようになった.つまり,C#からC++/CLIの関数が呼び出せるようになった.ただし,まだCLI→DLLの接続はできていない.

  

VS 2022を起動して準同型検定CSを開く

VS 2022を起動して準同型検定CSを開いてみた.以下のようなエラーが大量に出ている.I:\Visual Studio 2022\Web\Snippets\HTML\1041\ASP.NET\gridview.snippet: 言語の属性がないか、または無効です。

スニペットというのは,エディット中に表示されるヒントのようなものと思われるので,多分ビルドには直接影響しないのではないかと思う.⇒ビルドしてみたが,例のエラーが再発している.

重大度レベル    コード    説明    プロジェクト    ファイル    行    抑制状態 エラー    C1189    #error:  “This library now requires a C++11 or later compiler – this message was generated as a result of BOOST_NO_CXX11_REF_QUALIFIERS being set” ElsieProject C:\Users\babalabo\vcpkg\installed\x86-windows\include \boost\multiprecision\detail\check_cpp11_config.hpp 34   

ネット情報によると,boost のバージョンを戻すと解消するようなことが書いてあったのだが… どうも厄介なものを背負い込んでしまったようだ.boost を使うために vcpkg などというよく分からないものまで導入しているのだが… C#ではBigIntegerが使えるので,ELSIEをまるごとC#をに移植するという方が正解だったかも… ⇒この障害は準同型検定CS 2023-09-19-1から始まっている.とりあえず,準同型検定CS 2023-09-19まで戻ってみよう.2023/09/19のログのタイトルは「C#からC++の関数を呼び出すサンプル」だ.ともかくここにラッパクラスのプロジェクトを導入してみよう.

D8016 コマンドラインオプション /ZIと/GLは同時に指定できません.⇒デバッグライブラリの使用とUnity(JUMBO)ビルドを有効をオフにして解消した.⇒いや,解消していない.「

/ZIプロジェクトのプロパティの[C/C++]→[全般]のデバッグ情報で「エディット コンティニュのプログラム データベース(/ZI)」で指定します。/GLは同じく[C/C++]→[最適化]のプログラム全体の最適化で「はい(/GL)」で指定します。この二つはデフォルトでプロジェクトを作成すると同時に指定される事はないはずですが、変更しましたか?
Debugモードならばプログラム全体の最適化は指定されてないはずです。またReleaseモードでは、デバッグ情報の形式は「プログラム データベース(/Zi)」になっているはずです。」ということのようだ.⇒デバッグモードでC/C++→全般→デバッグ情報の形式→なしとして解消.

LNK200 未解決の外部参照が3件出た.calloc_dbg, CrtDFbgReport, invalid_parameter.⇒構成プロパティ→詳細→デバッグライブラリの使用→はいで解消した.

警告:LINK : warning LNK4075: /INCREMENTAL は /LTCG の指定によって無視されます。リンカー→全般→インクリメンタルリンクを有効にする→いいえで解消した.

ソリューションにCLR クラスライブラリ(.NET)タイプの新しいプロジェクトを追加して,ElsieProjectとする.さらに,プロジェクトの依存関係を設定.準同型検定CS→参照→参照の追加→プロジェクト→ElsieProjectにチェックを入れる.⇒構成マネージャ→アクティブソリューション構成→準同型検定CS→プラットフォーム→Any CPUを新しいプラットフォーム→x86に切り替え.C#プロジェクト出力先の変更:プロパティ→ビルド→出力パスをbin\x86\Debug\→..\Debug\,bin\x86\Release\→..\Release\ に変更.C++の出力先と一致させる.PsiNumber.hに以下の行を追加.

#ifdef NATIVEFUNC_EXPORTS #define NATIVEFUNC_API __declspec(dllexport) #else #define NATIVEFUNC_API __declspec(dllimport) #endif namespace Native { // num個のint配列srcから、最大値とそのインデックスを探す関数です NATIVEFUNC_API void Max(int* src, int num, int* mx, int* mxIndex); }

C++側のビルドは通ったが,ソリューションのビルドで警告が大量発生.“Microsoft.CSharp” の異なるバージョン間で、解決できない競合が見つかりました。C# V4.0とV5.0が競合している.System.Net.Http” の異なるバージョン間で、解決できない競合が見つかりました。という警告も出ている.⇒ツール→NuGetパッケージマネージャ→パッケージマネージャコンソールでUpdate-Package -reinstall コマンドを実行して解消したようだ.

ラッパークラスなるものを導入してみよう

ともかく,ラッパークラスなるものを導入してみよう.というか,DLLの部分,つまりELSIEの部分はCLIでビルドしているので,そのまま使えるのではないだろうか?C++部にnakespace ELSIE を立て,その中に class ElsieClass というのを置いてみたが,C#からこの namespace にアクセスできない.つまり,見えていない.⇒C#から、C++の関数の実行(関数)のアドバイスに逐語的に従ってみよう.

▲PCH 警告: ヘッダーの停止はグローバル スコープにある必要があります。IntelliSense PCH ファイルは生成されませんでした。というエラーが出る.This library now requires a C++11 or later compiler

どうもこのエラーはboostに関係するもののようだ.これまでは問題なく動いていたのだが… boostをインストールしたときは,VS2017の環境で動かしていたのだろうか?VS2019に移っても当初は特に問題なかったような気がするのだが… VSインストーラでいろいろ試してみたがどうも効果がない.C++ 11 というのをインストールするにはどうしたらいいのか,が出てこない.よく分からないが,VS2022をインストールしてみることにする.⇒Cドライブに56.83GBの空き容量が必要だ.ドライブは変更可能なので,Iドライブを使うことにした.ほとんど使っていないので,750GB以上ある.⇒インストールは完了した.また,明日.

C#からC++の関数を呼び出すサンプル

以下の記事を参考にして,C#からC++の関数を呼び出すサンプルを作ってみた. C#から、C++の関数の実行(関数)

とりあえず,説明通り動いた.この記事ではネィティブコードのDLLを生成し,それを呼び出すラッパクラスを別プロジェクトでビルドしている.DLLのヘッダでは以下を宣言して使っている.

#ifdef NATIVEFUNC_EXPORTS
#define NATIVEFUNC_API __declspec(dllexport)
#else
#define NATIVEFUNC_API __declspec(dllimport)
#endif

namespace Native
{
     // num個のint配列srcから、最大値とそのインデックスを探す関数です
     NATIVEFUNC_API void Max(int* src, int num, int* mx, int* mxIndex);
}

ラッパクラスの関数の中では以下のように生ポインタをピン止めするという操作を行っている.

void Wrapper::WrapperClass::Max(array<int>^ src, int num, int% mx, int% mxIndex)
{
     // ★ここがポイント
     // 実行中、ガベージコレクションされないように、pin_ptrを使って固定する
     pin_ptr<int> psrc = &src[0];
     pin_ptr<int> pmx = &mx;
     pin_ptr<int> pMxIndex = &mxIndex;

    // 自作関数実行
     Native::Max(psrc, num, pmx, pMxIndex);

    // ★ここがポイント
     // 固定解除
     psrc = nullptr;
     pmx = nullptr;
     pMxIndex = nullptr;
}

C++プロジェクトでは「シンボルのエクスポートにチェック」となっているが,VS2019の画面ではこのようなオプションは表示されない.また,C++では出力ファイルをDLLとしているのに,ラッパクラスのプロパティ → リンカー → 入力 →追加の依存ファイルではLIBが設定されているというのもよく分からないところだ.

自己同型検定を一晩走らせてA4同型写像24個を出力した

A4の自己同型検定を一晩走らせて,恒等写像を含めて24個の同型写像が出力されたが,完了までにはほど遠い.停止させてチェックしたところ「i」までは進んでいたので,半分くらいこなしたというところだろうか?少なくともS5くらいまでは計算できるようにしたいのだがほど遠い話だ.ELSIEは一般の行列の同型判定プログラムだが,準同型検定CSは本来群の検定を目的としたものであるから,単位元の存在は仮定してよいはずだ.従って,全置換のうち,先頭がaであるものだけを検査するというのでよいのではないだろうか?これならほどほどの時間で完了できる.A4なら10分も掛からないのではないだろうか?S5となるとそれでもほぼ不可能だが,S4なら何とかなるかもしれない.

どう改造すればよいか?ArrayList Createで渡す配列を最初から短くしておくのが一番手っ取り早い.Permutation.Printでリストを生成するときに書き戻せばよいのではないか?いや,それより確実なのは,全単射の生成で置換リストを生成するときに付け加える方が分かり易い.⇒しかし,この場合は全置換リストを生成する必要があるのではないか?対称群を生成するときなどは完全な置換リストが必要になる.

「置換リストを使う」設定で,自己同型検定→群同型判定で例外が発生する.⇒全単射の生成で落ちる.置換リスト = new string[m, n];で墜ちている.m=479001600, n=12だ.sizeof(string)は取れないので,sizeof(long)で代用すると,18,446,744,072,449,064,960で到底カバーできる数字ではない.

「置換リストを使う」設定のときの論理は現行のママとし,使わないときには Permutation.Creatの引数で調整することにする.⇒実装した.A4でテストしてみる.⇒最初の3つまではすぐに出たが,その後が続かない.相当な時間が掛かりそうだ.13:10開始ということにして時間を測ってみることにする.⇒完了した.所要時間は1時間半.まぁまぁの成績と言ってよいだろう.

#1 Permute 自己同型:b,c,d,e,f,g,h,i,j,k,l,
#2 Permute 自己同型:b,c,i,h,g,k,j,l,e,f,d,
#3 Permute 自己同型:b,c,l,j,k,f,e,d,h,g,i,
#4 Permute 自己同型:c,b,d,f,e,j,k,l,g,h,i,
#5 Permute 自己同型:c,b,i,g,h,e,f,d,k,j,l,
#6 Permute 自己同型:c,b,l,k,j,h,g,i,f,e,d,
#7 Permute 自己同型:e,g,d,k,c,h,f,l,b,j,i,
#8 Permute 自己同型:e,g,i,b,j,c,k,d,f,h,l,
#9 Permute 自己同型:e,g,l,f,h,j,b,i,k,c,d,
#10 Permute 自己同型:f,j,d,h,b,k,e,i,c,g,l,
#11 Permute 自己同型:f,j,i,e,k,g,c,l,h,b,d,
#12 Permute 自己同型:f,j,l,c,g,b,h,d,e,k,i,
#13 Permute 自己同型:g,e,d,c,k,b,j,i,h,f,l,
#14 Permute 自己同型:g,e,i,j,b,f,h,l,c,k,d,
#15 Permute 自己同型:g,e,l,h,f,k,c,d,j,b,i,
#16 Permute 自己同型:h,k,d,g,j,e,b,l,f,c,i,
#17 Permute 自己同型:h,k,i,f,c,j,g,d,b,e,l,
#18 Permute 自己同型:h,k,l,b,e,c,f,i,g,j,d,
#19 Permute 自己同型:j,f,d,b,h,c,g,l,k,e,i,
#20 Permute 自己同型:j,f,i,k,e,h,b,d,g,c,l,
#21 Permute 自己同型:j,f,l,g,c,e,k,i,b,h,d,
#22 Permute 自己同型:k,h,d,j,g,f,c,i,e,b,l,
#23 Permute 自己同型:k,h,i,c,f,b,e,l,j,g,d,
#24 Permute 自己同型:k,h,l,e,b,g,j,d,c,f,i,

いよいよ,ELSIEの登場ではないだろうか?ELSIEはプロジェクトとしてはすでに組み込まれているのだが,C#からどう呼び出せばよいのだろうか?マネージドからアンマネージドなので何かラッパーのようなものが必要になるのではないかと思うのだが… ⇒そもそも,#include プリプロセッサディレクティブがない.⇒少なくとも,下記のような仕掛けが必要なようだ.

extern “C”             //No name mangling
__declspec(dllexport)  //Tells the compiler to export the function
int                    //Function return type    
__cdecl                //Specifies calling convention, cdelc is default,
                        //so this can be omitted
test(int number){
     return number + 1;
}

ゼルコバの木ではどんなことをやっていたのか見てみよう.ExporoClasses.hというヘッダファイルがある.冒頭で,

#if defined(ZELKOVA_EXPORTS)
#define _EXPORTCLASS __declspec(dllexport)
#else
#define _EXPORTCLASS __declspec(dllimport)
#endif /* ZELKOVA_EXPORTS */

でEXPORTCLASS を定義している.ZELKOVA_EXPORTSを使って,dllexportとdllimportを切り分けている.どういう使い方をするのかは,いまいち分からない.ZELKOVA_EXPORTSは comdebug.h で定義されている.コメントに「COMDEBUG: _EXPORTCLASSの出入りの方向を決める:この行はExportClasses.hの前に置かなくてはならない!」とある.ExporoClasses.hには外部インタフェース用のクラスが各種定義されている.すべて,class _EXPORTCLASS PERSONAL のようにEXPORTCLASS として立てられている.

もうひとつ,ZelkovaExports.hというヘッダファイルがある.ここでも同種のマクロが定義されている.

#if defined(ZELKOVA_EXPORTS)
#define _EXPORT extern “C” __declspec(dllexport)
#else
#define _EXPORT extern “C” __declspec(dllimport)
#endif // ZELKOVA_EXPORTS

このヘッダファイルには,

_EXPORT long __stdcall GetViewRect(CRect *viewrect);

のような形式でDLLインタフェース関数が列挙されている.この関数の実装は,KeizuDLL.cppにあり,

long __stdcall GetViewRect(CRect *viewrect)
{
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     probe(GetViewRect)
     ASSERT_NEVER (!TreeView)
     *viewrect = TreeView->ViewRect();
     return 0;
}

のようなややこしいことをしている.なぜか,AFX_MANAGE_STATE(AfxGetStaticModuleState());のようなお呪いが必要とされている.このような宣言はextern “C” {}というブロックの中で行われているという点もポイントだ.

ZelkovaGC.hでは,namespace ZelkovaBetaというのが定義されている.このヘッダファイルにはExportClasses.hとZelkovaExports.hが#include されていて,参照できるようになっている.namespace ZelkovaBetaの中にはVBから参照されるクラスが定義されている.VBとC#はほぼ同等なので,ここまでやらなくてはならないのではないか?DLL内の関数名などをどう参照すればよいのか?DLLをリンクするだけで参照できるようになるのだろうか?namespace はC++で宣言できる.これだけで済むのなら好都合なのだが…

もう一つ問題がある.matrixはhugenumの行列になっている.これをcpp_intに切り替えるか,ないしC#に持ち出してBigIntegerが使えるようにしなくてはならない.ELSIEは4つのC++ファイルからなっている.これをまるごとC#に移すというのはちょっと二の足を踏んでしまう.まず,最初にこれをcpp_intに置き換えるところから始めた方がよいのではないか?仮に最終的にC#に持ち出すことになったとしても,そこまでやってあれば,かなり対応し易くなるはずだ.しかし,そもそもmatrixをhugenumにする必要が本当にあるのだろうか?PSI数などが大きくなるのは当然であるとしても,行列本体に巨大数を格納するというのは必要なのだろうか?

matrixとarrayはhugenumだが,それと別にMatとVectというのがある.これらはintの配列だ.ISOMORPHというクラスにはarrayが3つ,matrixが3つも含まれている.ISOMORPHのinitializeでは最初に引数で渡されたMatを内部のMatメンバーにコピーしている.つまり,検体となる行列は本来 int 配列なのだろう.従って,外部とのインターフェースはこの行列を渡せばよいということになるのではないか?⇒とりあえず,外部から2つのMatデータを与えられればMatrixComparisonと同等の検査を実行できそうだ.とりあえず,ELSIEの持っている6つの処理をC#から実行できるようにすればよいのではないか?