ソフトウェア開発者へ(重要)


 TRYCUT SDKの仕様は、設計初期段階ではC言語やC++言語で開発できるという ことを想定したものでしたが、途中からFortranでも開発可能にとの要望も出てきま した。これは意外でしたが、世の中の科学技術計算用ソフトの資産を生かすという ことを考えると、今でもFortranは無視できないだろうと判断いたしました。 そこで、少し欲をはり、それならば基本的にどんな言語からでも受け付ける仕様を 目指そうということで検討してきました。
 どんな言語と言いましてもWindows上での開発 言語で特に高速処理が要求され科学技術計算分野と考えるならば、言語は ほぼC,C++,DELPHI(PASCAL),FORTRAN ぐらいに絞られてしまいます。この中でもCはC++に包含されているような言語ですし、 Delphi(Pascal)はどちらかというとCやFORTRANの中間みたいな言語でもありますので、 まずはFORTRANを受け付けることができれば、ほぼ網羅できるだろうと考えてきま した。

 ところで開発者の観点では、どんな言語でもOKと書いていることに、 ほんとに大丈夫なの?と、少し技術的な問題点や疑問を感じる場合が出てくることも あるのではないでしょうか?
 DLLを作ってみたところで、そんなにうまくTRYCUTが 認識してくれるのだろうか? 具体的によくあるのがコンパイル、リンケージ 後に各関数のエントリー名が、ほんとにTRYCUTが認識できるものに なっているのだろうか否か? 各引数のスタックの持たせ方は一致する のか否か? といった技術的な問題点や心配が出てきたりすると思われ ます。経験の豊富な方であれば、なおさらそうかもしれません。

 そこで、TRYCUT開発者といたしましても、逆の立場で このような心配をなくすにはどうすればよいかということで、SDKの設計段階から かなり慎重に練ってきた経緯がありますので。そこの部分をこのページで説明さ せておいていただきます。

1. 関数の外部参照名(エントリー名)の違い
2. 引数の引渡し
3. 関数(手続き)の形式

★同一機能を、C,Delphi(Pascal),Fortranの3種類の言語 で書いた例(ソース)


1.関数の外部参照名(エントリー名)の違い

 この問題はC言語とC++言語を混在して開発したり、異種言語間の リンクを行なったり、また同じ言語でもメーカーの異なるコンパイラを 使ったりすると必ずと言って良いほど、出くわす問題です。
 普通C言語だけで開発している場合は、コンパイル後の 各関数のエントリー名は、ソースコードに記述した関数名と全く同じ、 もしくは、関数名の前に_(アンダーバー)が付加されるか、もしくは他にも パターンがあるかもしれませんが。基本的には, このあたりはコンパイラの内部仕様の問題で、 コンパイラごとに異なると想定しておく必要があります。

例えば、
同じ"TRMODINIT"でもMicrosoft系(VC,SDKなど)のC++やGNU C++で、DLLを作りますと、
TRMODINIT
のように関数名そのものになりますが、
BORLAND C++ では、以下のように
_TRMODINIT
先頭に_(アンダーバー)が付加されます。

 ですから、一般的に同じC言語でも異なるコンパイラで開発したオブジェクト やライブラリを混在させるとリンケージ時にうまく行かなくなることが 多いのです。コンパイラの中にはオプション設定により、このあたり の仕様をどちらにでも合わせられるようにしたものもありますが、 通常、初期状態で違いがあると、オプション設定で解決できる場合が あっても、そういうところの仕様まで理解するのは面倒なので、 最初にリンケージで失敗すると、その段階で諦めてしまうことが、かなり あると思います。
 ということもあり、TRYCUTでは、認識する時点で上記例のいずれ でも受け付ける処理を行なっています。ですから、今のところの判断では まずほとんどのコンパイラにて開発は可能かと思っています。万一異なる パターンが存在しましたらご連絡いただければ対応できるようにしたいと 考えています。

 またC++言語は、さらに悩ましい問題があり、関数名の後に"@"が付加され、 その後に引数スタックのサイズが挿入されるという変則的な内部仕様になってい ます。これは引数の異なる関数であれば同一名称も許されるなど、 言語として進化させるために避けられなかったフォローなのですが、従来から 普及してきた言語のCやFORTRANなどとは相性が合わなくなります。結局 C++言語側で関数名を明示的に認識する側も、単純な関数名そのものの 方が判りやすいわけで、通常はC言語の名称ルールに従う形で外部のDLL 内関数を認識させます。TRYCUT側もそうしています。
 そのかわり、もしDLL側がC++言語でコンパイルした場合は、 スケルトンの各ソースコードを見ていただければ判るように、以下のような 赤字部分のおまじないを行うことになります。 この手法は、一般的によく使われているようです。

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void TRMODINIT(int*);
__declspec(dllexport) void TRMODMENUITEM(int*,char *);
__declspec(dllexport) void TRMODPARAMETER(int*);
__declspec(dllexport) void TRMODMODWORK(double*,double*,double*,int*,int*,float*,char*,int*,int*);
__declspec(dllexport) void TRMODFREE();
#ifdef __cplusplus
}
#endif

 のように、もしこのソースコードがC++言語として、コンパイルされる 場合は、強制的にC言語から参照される関数として、定義されるというも のです。赤字部分の行があれば、CでもC++でも、どちらでコンパイル してもOKということになります。
(※最近のC++対応コンパイラでは、通常格張子が".cpp"であれば C++とみなされ、".c"であればC言語と勝手にみなしてコンパイルを 行なう場合が多いようです。)

 他の言語においては、例えばFortranの場合は、コンパイラの種類にも よるかもしれませんが、従来から基本的には小文字でも大文字でも、また混在して いたとしてもエントリー名は、大文字に統一されたりいたします。ですから、 逆に小文字のエントリー名が入っていると認識できなくなる可能性があるわけです。 なぜ本SDKの関数名が大文字だけで統一してあるかと言いますと、理由はただ ひとつ、このFortranでの開発を可能にしようとしたためです。


2.引数の引渡し

 異なる言語の関数同士は引数の引渡しにおいて、様々な問題を引き起こします。 本SDKの関数仕様は様々な観点でそれをフォローしています。

 a) アドレス渡しか、値渡しか
 通常Fortranでは引数はアドレス渡しにしか対応してこなかったことから、 Fortranでも違和感無く扱えるように、統一的にアドレス渡しにしています。最近の 新しい仕様のFortranではポインター(アドレス)も扱えるようになっているようですが、 まずFortranでポインターを駆使してコーディングしているケースは稀に見るケース だという判断をしています。ですから関数の引数は全てアドレス渡しに統一いたしま した。
 逆にCやC++のスケルトンでは、各引数が全て int *xxx や float *yyy のように なっていて違和感はあるかもしれませんが、まず機能的な問題にはならないはず です。

 b) 構造体を使わない
 CやPascal(Delphi)では常識的に使われてきた構造体は、引数の渡し方以前に、 大変悩ましい問題を抱えています。その内部的なデータの保持 形式がコンパイラの種類や、また極端な例ではコンパイラのバージョンでも異なって きたりするなどの問題があります。また通常Fortranでは構造体という概念は ありません。ですから少し冗長な引数列になっている部分もありますが、どんな 言語でもというコンセプトを守るために、関数の引数はすべて基本の型変数 (整数、実数、倍精度実数、文字列)だけにしています。

 c) 文字列変数の文字数の扱い
 Fortranのように文字変数がアドレスと長さを同時にペアで引き渡してしまう言語の 場合、そのアドレスの後に長さが連続して挿入されてしまうと、そうでない言語が参照 する場合は、その変数のうしろの引数では引渡しポイントがズレてしまうという問題を 引き起こします。
 この問題に関してのフォローは、特にSDK側の仕組みとして対応できるものでは ありませんが、各コンパイラ側のオプションにより対処可能な場合が多いです。例えば Compaq Visual Fortranでは、コンパイラの設定 "External Procedures"の中に "String Length Argument Passing:"なる項目があり、文字列長さは全ての 引数の後に廻すという指定"After All Args"を選択しておくことで解決できます。
 Delphi(Pascal)においては、少し意味合いが変わりますが、長さを持たない ポインター型(PChar)を利用することが一般的に推奨されています。
こちらのサンプルtrmod.dprでも PChar型で代用しています。


3.関数(手続き)の形式

 これもFortranを意識したものですが、Fortranで言うところの関数(手続き)は 一般的によく使用されるSUBROUTINE(サブルーチン副プログラム)と FUNCTION(関数副プログラム)ですが、どちらかと言うとSUBROUTINEで 記述されることが圧倒的に多いものと思われます。ただし、これは戻り値を指定する ことができません。すなわちCで言うところの全てvoid関数となることから、少し わずらわしいのですが、戻り値の必要なものは引数でエラーコードを返すという 統一仕様にしています。ということでFortranで書く場合は、各関数を SUBROUTINEで記述することになります。結局この部分は従来のFortranの 流れに譲った結果です。
 また、 Delphi(pascal)で開発する場合も同様で、 procedure(手続き/戻り値無し)とfunction(関数/戻り値有り)があるのですが、 前者のprocedureで記述していただくことになります。

※C言語を基本にして書いていますので、どうしても「関数」という表現になって しまうのですが、プログラミング言語の世界では「手続き(procedure)」という 呼ばれ方が一般的です。


SDKトップページへ
トップページへ