Euphoriaの特定動作環境に関する議論


1. はじめに

Rapid Deployment Softwareでは現在、Euphoriaは4種の異なる動作環境(platforms)に対応しています。 将来は、さらに動作環境が追加されていく予定です。

第1の動作環境はDOS32と呼ばれているものでありDOSオペレーティングシステムに依存しますが、CPUは32bitプロテクトモードで動作します。

第2の動作環境はWIN32と呼ばれるものであり、Windows 95/98/MeさらにはNT/2000/XP/Vista/7以降の32bit版Microsoft Windowsオペレーティングシステムで動作します。

第3の動作環境はLinuxです。LinuxはUNIXオペレーティングシステム(System V)をお手本にしています。これは最近は非常にパソコンで人気があり、ネットブックやネットトップなどで採用されています。Caldera, Debian, Knoppix, Mandriva, Red hat, Slackware, Turbo, Ubuntu, Vineなどのディストリビュージョンがあり、比較的廉価または無償にて入手できるオープンソースのオペレーティングシステムです。

第4の動作環境はFreeBSDです。FreeBSDはUNIXオペレーティングシステム(BSD)をお手本にしています。これはオープンソースでありインターネットサーバーマシンでは非常に有名です。

なお、有志によりEuphoriaはSun SolarisやHP-UXなど他の動作環境へ移植されています。

DOS32+WIN32版Euphoriaではインタプリタとして3種の.exeファイルがインストールされます。 第1のものはex.exeと呼ばれています。これはEuphoriaプログラムをDOS32動作環境で動作させます。第2のものはexw.exeです。これはEuphoria GUIプログラムをWIN32動作環境で動作させます。第3のものはexw.exeです。これはEuphoria コンソールプログラムをWIN動作環境で動作させます。拡張子.exのEuphoriaプログラムはDOS32動作環境で実行されることを想定しており、拡張子.exwのEuphoriaプログラムはWIN32動作環境で実行されることを想定しています。

Linux版Euphoriaの.tarファイルパッケージにはexuのみ含まれています。これは EuphoriaプログラムをLinux動作環境で動作させます。拡張子.exuのEuphoriaプログラムはLinuxまたはFreeBSD動作環境で実行されることを想定しています。

FreeBSD版のEuphoriaをインストールするときは、まず最初にLinux版のEuphoriaをインストールしてからexuをFreeBSD版のexuを上書きします。

大半のEuphoriaプログラムは変更をせずに2、3または4種の全動作環境で実行できます。拡張子を選択してプログラムの対象動作環境を表すべきです。全てのEuphoriaインタプリタで任意のEuphoriaファイルを実行できますが、各インタプリタにプログラムのファイル名だけを与えるとインタプリタで自身のファイル名に対応する拡張子でプログラムを検索するため、場合によってはプログラムのファイル名と拡張子(.ex, .exwまたは.exu)をインタプリタへ指定する必要があります。

時として、コードの大部分が全動作環境で互換性があると判明していることがありますが、対象動作環境ごとの差異を吸収するため、いくつか小規模の動作環境依存コードを書く必要があります。その場合はplatform()組み込み関数を使用して現在どの動作環境で動作中であるか検出します。注意事項としてLinux/FreeBSDの双方においてシステムは類似しているためplatform()は同一の値(3)を返します。


2. DOS32動作環境

プログラミング初心者ならば、DOS32動作環境であるex.exeから始めたい場合があります。Windows GUIプログラミングに飛び込む前に、Euphriaにおける本質的主要部について十分理解する必要があります。Windows(XP/Vista/7を含む)の全バージョンでは、コンソールウィンドウとしてコマンドプロンプトを開いてDOSプログラムを実行できます。また、DOSBOXでも同様のことが可能あり、DOS32以外でもDOS32プログラムを動作させることができます。

Euphoriaプログラムは32bitプロテクトモードで動作しており、マシン上のメガバイト級の全メモリーにアクセスできます。多くのDOS専用プログラミング言語で開発されたアプリケーションは16bitリアルモードでしか動作しないという制限があります。これは一度に640KB以上のメモリーにアクセスできないということです。この場合はマシンに256MB以上のメモリーが搭載されている場合であってもプログラムで640KBを使い切ってしまうとメモリー不足になります。さらにQBasicではプログラムで使用できるメモリーは160KBまでという制限があるため、さらに状況が悪化します。

DOS32プログラムはテキストモードまたピクセルグラフィックスモードの双方でスクリーンを切り替えることができ、さらにEuphoriaは双方で使用できるライブラリルーチンを提供しています。稀に任意のDOS割り込み呼び出しが必要なときがありますが、その場合は dos_interrupt()を使用します。特別なメモリー領域に対してpeekおよびpokeを使用することにより高速なグラフィック処理を実現でき、さらにシステムの低レベル層(ハードウェア)の細部にもアクセスできます。

Windows 95以降のシステム下で動作するDOS32版では、Euphoriaは長いファイル名を扱うことができ、プログラムは長いファイル名のファイルを開いて読み書きできますが、新規作成はできません。

Windows外の純粋なDOS環境下では、システムスワップファイルはありませんがex.exeに内蔵されているDOSエクステンダがプログラムで利用可能なスワップファイルを作成します。EuphoriaのプログラムがDOS下で動作を開始したときスワップファイルが作成され、プログラム終了時にスワップファイルは削除されます。実際はスワップファイルの大きさは0byteから始まり、スワッピングが必要なときだけ大きさは増えます。環境変数TEMPまたはTMPで指定された(ハードディスクの)ディレクトリにスワップファイルは作成されます。いずれの変数も設定されていないときは、ex.exeまたはバインドされたEuphoriaプログラムの.exeディレクトリ内に作成されます。次のように環境変数CAUSEWAYを設定することによりスワップファイル作成先のディレクトリを強制指定することできます:

        SET CAUSEWAY=SWAP:path
pathにはスワップファイル作成先のディレクトリのフルパスを指定します。また、次のようにするとDOSスワップファイルの作成を禁止することができます:
        SET CAUSEWAY=NOVM

ディスクスワップ実行中はプログラムは正常に動作しますが、動作速度は低下します。良い解決策は大量の拡張メモリーを予約しようとするSMARTDRVのキャッシュサイズおよび他のプログラムで使用するメモリー量を減らして拡張メモリーを開放することです。

なお、空きディスク容量がマシンに搭載されているRAM容量より少ないときは、スワップファイルは作成されません。


3. WIN32動作環境

Euphroia WIN32版(exw.exe)はDOS32版と多くの共通点があります。WIN32ではマシンに搭載されている全てのメモリーにアクセスできます。大部分のライブラリルーチンは、各動作環境で同様の方法で動作します。既存のDOS32テキストモードプログラムの多くは一切の変更なしでexwを使用して動作させることができます。exwはプログラムをコマンドラインから実行でき、テキストを標準DOSウィンドウ(通常は25行 x 80桁)に表示します。DOSウィンドウはWindowsの専門用語ではコンソールとして知られています。EuphoriaはDOS32テキストモードプログラミングからWIN32コンソールプログラミングへの移行を非常に容易なものにします。さらにWIN32 C言語関数呼び出しを後日追加できるため、これにより必要に応じて真のWindows GUIウィンドウプログラムを作成することができます。

最初にEuphoria WIN32版のプログラムがスクリーンに何かを出力するか、キーボードから入力を読み取るときにコンソールウィンドウは自動的に表示されます。コンソールウィンドウは標準入力から読み取るときや標準出力に書き込むとき、これらがファイルへリダイレクトされたときに表示されます。コンソールウィンドウはプログラム実行終了時またはfree_console()を呼び出したときに閉じられます。利用者がコンソールの表示や処理状況または結果を読むために、終了前にプロンプトを表示して利用者からのキー入力を待機する必要があります。プログラム終了時にコンソールがすぐ閉じられてしまうのを防止するために、例えば次の命令文を記述する場合があります:

        if getc(0) then
        end if
これは利用者がなにかキーを入力するまで待機します。

新規コンソールウィンドウを起動することなくEuphoriaプログラムを実行したいときはexwc.exeを使用します。これはDOSプログラムex.exeを使用したときのように、現在のコンソールウィンドウ内で処理を行います。

WIN32では、長いファイル名に完全対応しており読み書きや作成ができます。


3.1 高レベルWIN32プログラミング

デイビッド・キュニー(David Cuny), デリクパーネル(Derek Parnell), ジュディス・エヴァンズ(Judith Evans)およびその他多くの人々のおかげでEuphoriaでWindows GUIアプリケーション開発に利用できるWin32Libと呼ばれるパッケージがあります。これは習得と利用が容易であり、良質な説明書と多数の小さなプログラム例で構成されています。これはEuphoriaウェブサイトからWin32LibとジュディスによるIDEをダウンロードできます。近年では、Win32Libと似たアンドレア・サイニー(Andrea Cini)が開発したEuWinGUIと呼ばれるパッケージがあります。これもEuphoriaウェブサイトからダウンロードできます


3.2 低レベルWIN32プログラミング

WIN32の低レベル層のアクセスが許可されているため、Euphoriaは任意のWIN32 API .dllファイルまたは、あなたや他の人が作成した32bit Windows .dllファイルから任意のC言語関数を呼び出す仕組みを提供しています。WindowsにはEuphoriaルーチンを呼び出すことのできるコールバック機構があります。グラフィカル・ユーザ・インタフェースを作成するとき、コールバックは必要となります。

WIN32動作環境を完全活用するためには、WIN32アプリケーション・プログラミング・インタフェース(API)の解説とAPIで使用されるC言語構造体に関する解説がある32bit Windowsプログラミング技術書を必要とします。技術書として多くのWindows版のプログラミングツールで利用できるマイクロソフト製のWIN32.HLPファイルがあり(訳注:2010年現在ではBorland ftpに存在したWIN32.HLPは配布停止されています)、さらにMSDN Library(日本語版)やMicrosoft Windows SDK(日本語版)のリンク先より豊富な技術資料を入手することができます。さらにWIN32プログラミングを対象としたC/C++言語書籍が多数あります。Euphoria WIN32プログラミングの領域では、これらの存在する書籍の内容のほとんどを適用することができます。良書の一例は次のとおりです(訳注:この文章が書かれた年代が古いため改訂版が出ています):

Programming Windows
by Charles Petzold
Microsoft Press

また、下記にもWIN32 APIに関する秀逸なウェブサイトがあります。

そのほかにもEuphoriaアーカイブウェブページ - "ドキュメンテーション"を参照してください。


4. LinuxおよびFreeBSD動作環境

Linux/FreeBSD版EuphoriaはWIN32/DOS32と一部の機能が共通しています。

WIN32/DOS32と同様に、任意の行また桁の位置に複数の色を使用して、コンソールまたはxtermウィンドウでテキストを出力することができます、

WIN32と場合と同じように、C言語共有ライブラリを呼び出すことができ、C言語コードからEuphoriaコードをコールバックすることができます。

Linux/FreeBSD版EuphoriaはDOS32のように統合されたピクセルグラフィックスモードに対応していませんが、ピーター・エバーレイン(Pete Eberlein)によるEuphoira用svgalibインタフェースがあります。

GTK GUIライブラリ向けのアーヴ・マリン(Irv Mullin)によるEuGTKインタフェースまたはマット・ルイス(Matt Lewis)によって開発されたWxWidgets向けのWxEuphoriaの両方が利用可能であり、WxEuphoriaはWindowsでも動作可能です。

DOSまたはWindowsからLinuxまたはFreBSDへコードを移植するときは次の差異に注意してください:

  • graphics.eにおいて割り当てられている主要な16色にはいくつか相違点があります。graphics.eで定義されている定数を使用する場合は問題は発生しません。もし、色番号をハードコードした場合は青と赤などが交換されてしまったかのよう見える場合があります。

  • 特殊キーであるHome, End, 矢印などのキーコードには相違点があり、さらにXTERM下で実行すると、いくつか追加の相違点があります。

  • LinuxではEnterのキーコードは10(LF : 行送り)ですが、DOS/Windowsでは13(CR:キャリッジリターン)です。

  • Linux/FreeBSDではファイルパスは '/' (スラッシュ)ですが、DOS/Windowsでは '\\' (バックスラッシュ)を使用します。

  • 非常に特殊なものであるdos_interrupt()は明らかにLinux または FreeBSDでは動作しません。

  • system() または system_exec()によりDOSコマンドを呼び出している箇所はLinux/FreeBSDの対応するコマンドへ変更する必要があります。すなわち、"DEL" は "rm"となり、"MOVE" は "mv"となります。


5. C言語コードのインタフェース(WIN32, Linux, FreeBSD)

WIN32, LinuxおよびFreBSDではEuphoriaコードによるC言語コードのインタフェースの記述ができます。EuphoriaプログラムからC言語関数の呼び出し、C言語変数の読み書きができます。C言語ルーチンからもEuphoriaルーチンを呼ぶことができます("コールバック")。C言語コードはWIN32ダイナミックリンクライブラリ(.dllファイル)、またはLinuxおよびFreeBSDでは共有ライブラリ(.soファイル)である必要があります。.dllおよび共有ライブラリのインタフェースにより、システムにある全てのプログラミング・インタフェースにアクセスですることができます。

Euphoria→C言語トランスレータを使用すると、EuphoriaルーチンをC言語ルーチンにトランスレート(変換・翻訳)してから、.dllまたは.soファイルとしてコンパイルします。これらコンパイル済みEuphoriaルーチンにEuphoriaアトム型とシーケンス型を渡すことができ、返値をEuphoriaデータ型として受け取ることができます。一般的にトランスレート/コンパイルされたルーチンはインタプリタ上で動作するルーチンと比較して非常に高速な動作をします。 詳しい情報はトランスレータを参照してください。


5.1 C言語関数の呼び出し

.dllまたは.soファイル内のC言語関数を使用するには次の手順で呼び出す必要があります:

1. euphoria\include\dll.eにあるopen_dll()を呼び出してC言語関数を含む.dllまたは.soファイルを開きます。
2. dll.eにあるdefine_c_func()またはdefine_c_proc()を呼び出してC言語関数を定義します。この番号は引数の型だけではなく同様に型の値が返されたときにEuphoriaにも通知されます。

今のところEuphoriaはC言語整数およびポインタ型の引数および返値に対応しています。同様に浮動小数点の引数と返値も対応しています(C言語double型)。C言語構造体を値として渡したり関数の結果を構造体として受け取ることはできませんが、構造体のポインタを渡したり返値として構造体のポインタを受け取ることができます。C言語構造体を値として渡すことはオペレーティングシステムの呼び出しにおいて稀に必要なときがあります。

Euphoriaは全てのEuphoriaデータ形式に対応しています。 - トランスレート/コンパイル済みのEuphoriaルーチンの引数としてアトムおよび任意の複雑なシーケンスに対応しています。

3. C言語関数をc_func()またはc_proc()で呼び出します。

用例:
include dll.e

atom user32
integer LoadIcon, icon

user32 = open_dll("user32.dll")

-- ルーチン名はuser32.dllにある"LoadIconA"です。
-- これはポインタとint型の引数を持ち、返値はint型です。
LoadIcon = define_c_func(user32, "LoadIconA",
                         {C_POINTER, C_INT}, C_INT)

icon = c_func(LoadIcon, {NULL, IDI_APPLICATION})

c_func(), c_proc(), define_c_func(), define_c_proc(), open_dll()などの詳細はlibrary.doc - C言語の関数の呼び出し(WIN32/Linux)を参照してください。さらに、demo\win32 または demo\linux にプログラム例があります。

Windowsでは2つ以上のC言語呼び出し規約があります。全てのWindows APIルーチンは__stdcall規約を使用しています。しかし多くのC言語コンパイラでは __cdecl規約が規定値となっています。__cdeclでは引数として変数の番号を渡すことが許されています。Euphoriaでは__stdcallの使用を前提としていますが、C言語ルーチンを__cdeclで呼び出すときは'+'符号をc_proc() および define_c_func()で定義するルーチン名の先頭に付加します。例えば上述の用例では"LoadIconA"の代わりに "+LoadIconA"とします。

.dllファイル上で右クリックして"QuickView" (システムによります)を選択することで.dllファイルを調べることができます。これによりC言語ルーチンの.dllエクスポート関数一覧を参照することができます。

.dllファイルから特定のWIN32 C言語関数を見つけるにはeuphoria\demo\win32\dsearch.exwを実行します。


5.2 C言語変数へのアクセス

define_c_var()を使用することによりC言語変数のアドレスを取得できます。また、peek()およびpoke()を使用することによりC言語変数の値にアクセスできます。


5.3 C言語構造体へのアクセス

多くのC言語ルーチンでは構造体のポインタを渡す必要があります。擬似C言語構造体をメモリーブロックに割り当てることにより使用することができます。このアドレスはallocate()から返されC言語ポインタとして渡すことができます。

C言語の構造体メンバを読み書きするにはpeek() および poke(), または peek4u(), peek4s(), および poke4()を使用します。構造体の空き領域を確保するにはallocate()を使用します。なお、C言語の構造体メンバのオフセットを計算する必要があります。これは一般に容易なことであり、通常はC言語では変数は全て4byteからなるため構造体の代入には4byteが要求されます。このように、C言語ではint型, char型, unsigned int型,全てのポインタ型などは4byteの値をとります。もしC言語の宣言がこのようであるならば:

        // 警告 ここからはC言語コードです!!

        struct example {
            int a;           // offset  0
            char *b;         // offset  4
            char c;          // offset  8
            long d;          // offset 12
        };

"構造体の用例(struct example)"において空き領域を確保が必要です:

        atom p

        p = allocate(16) -- "構造体の用例(struct example)"においての大きさ

常にallocate()によって得られるアドレスは最小で4byte列です。WIN32構造体が4byte境界線から開始すると想定されているため、これは実用的です。C言語構造体内のフィールドの大きさとして4バイト以上のあるものはメモリーの4byte境界線から開始する必要があります。2byteフィールドは2byte境界線から開始する必要があります。これを実現するには構造体内に小さな隙間を残す必要があります。実際のところ多くの構造体ではフィールドの90%は4byteポインタまたは4byte整数として一列に並ぶため困難ではありません。

このようにしてフィールドを設定することができます:

        poke4(p + 0, a)
        poke4(p + 4, b)
        poke4(p + 8, c)
        poke4(p +12, d)
このようにしてフィールドを読むことができます:
        d = peek4(p+12)
助言:
可読性を向上するために、オフセット・フィールドに対応するEuphoria定数を作成します。詳しくは後述の用例を参照してください。

用例:
constant RECT_LEFT = 0,
         RECT_TOP  = 4,
         RECT_RIGHT = 8,
         RECT_BOTTOM = 12
         
atom rect
rect = allocate(16)

poke4(rect + RECT_LEFT,    10)
poke4(rect + RECT_TOP,     20)
poke4(rect + RECT_RIGHT,   90)
poke4(rect + RECT_BOTTOM, 100)
     
-- rectの構造体ポインタを渡します。
-- hWndはウィンドウ"ハンドル"です。
if not c_func(InvalidateRect, {hWnd, rect, 1}) then
    puts(2, "InvalidateRect failed\n")
end if
EuphoriaのコードでC言語ルーチンおよびデータ構造にアクセスするのは少し面倒に見えますが、特に通常はWin32Lib, EuWinGUI, または Irv Mullin's X Windows libraryを使用してプログラムを小さな部品の集合によって構成します。これによりプログラムの大部分は純粋にEuphoriaを使用して記述できる関係で、C言語コードでの記述を強制されるより大きな優位性があります。


5.4 Euphoriaルーチンのコールバック

Windowsオペレーティングシステムでウィンドウを作成するときは、Euphoriaルーチンを呼ぶ必要があります。オペレーティングシステムのが提供しているルーチンを呼び出すのには慣れているがオペレーティングシステムが開発者側で用意したルーチンを呼び出す仕組みを使用したことがないDOSプログラマにとっては、これは奇妙な概念です。これを設定するには、ルーチンの32bit"コールバック"アドレスを取得してからWindowsに渡す必要があります。例えば(demo\win32\window.exwより):

        integer id
        atom WndProcAddress
        
        id = routine_id("WndProc") 

        WndProcAddress = call_back(id)
routine_id()はEuphoria関数または手続きの固有識別子として小さな整数値を返します。この値は後でルーチンを呼び出すときに使用できます。また、call_back()関数の引数としても使用できます。

上述の例では、WndProcAddressの32bitコールバックアドレスはC言語構造体に格納され、RegisterClass()関数を経由してWindowsに渡されます。 WindowsにEuphoriaルーチンであるWndProc()を呼び出す権限を得ることにより、いつでも利用者は特定のウィンドウクラス上の機能(イベント)を実行できます。主な機能としてマウスクリック、キー入力、ウィンドウのリサイズなどがあります。なお、一部始終はwindow.exwデモプログラムを参照してください。

注意事項:
次の条件を満たしているのであれば任意のEuphoriaルーチンのコールバックアドレスを取得することができます:
  • ルーチンは手続きではなく、関数である必要があります。
  • 0から9までのパラメータが必要です。
  • 全パラメータの型はシーケンスではなく、アトム(また整数など)である必要があります。
  • 返値の大きさは32bitまでの整数値である必要があります。
好きなだけコールバックアドレスを多数作成できますが、同じEuphoriaルーチンをcall_back()で何回も呼ぶべきではありません。 - 作成したコールバックアドレスごとに小さなメモリーブロックを必要とするからです。

Euphoriaルーチンに渡すことのできる値は任意の32bit正数アトムです。すなわち負数ではありません。望むならルーチンは巨大な正数は負数として解釈するようにすることもできます。実例として、C言語ルーチンが-1をEuphoriaルーチンへ渡そうとするならば、それは16進数のFFFFFFFFとなります。 もし渡された値が指定したパラメータの型と一致しないならば、Euphoriaは型検査エラーが発生しますが(with/without type_checkなどに依存します)、全パラメータをアトムとして宣言した場合はエラーは発生しません。

通常、上述のWndProc()の場合はWindowsはEuphoriaルーチンのコールバックを開始します。任意のC言語ルーチンからEuphoriaルーチンのひとつを呼び出すことが可能です。適切にC言語ルーチンを宣言したのち、それにコールバックアドレスを渡します。

次のWATCOM Cにおいての例はパラメータとしてコールバックアドレスのみ持ち、3パラメータのEuphoriaルーチンを呼び出します:

        /* 1パラメータのC言語ルーチンでありEuphoriaから呼びされます。 */
        unsigned EXPORT APIENTRY test1(
                 LRESULT CALLBACK (*eu_callback)(unsigned a,
                                                 unsigned b,
                                                 unsigned c))
        {
            /* 3パラメータのEuphoriaルーチンでありeu_callback_pointerを経由して呼び出されます。 */
            return (*eu_callback)(111, 222, 333); 
        }
上述のC言語コードの宣言は単一のパラメータをもつtest1を外部から呼ぶことのできるC言語ルーチンとして宣言します。単一のパラメータは負数ではないパラメータをもつルーチンへのポインタです。 - すなわち、Euphoriaのルーチンです。

WATCOM Cでは、"CALLBACK"は"__stdcall"と同一です。これはWIN32 APIルーチンを呼び出すのに使用される呼び出し規約であり、またEuphoriaルーチンへのC言語ポインタも同様に宣言される必要があり、さらにEuphoriaルーチンが.DLLに変値を返そうとするとエラーになります。

もしEuphoriaルーチンを__cdecl規約を使用して呼び出す必要がある場合は、次のようにcall_back()を呼び出す必要があります:

        myroutineaddr = call_back({'+', id})
プラス記号とブレースブラケットは__cdecl規約を意味します。単にブレースブラケットがない場合は__stdcall規約です。

上述の例では、Euphoriaルーチンに引数として3つの値111, 222および333が渡されます。Euphoriaルーチンはtest1に値を返します。この値はtest1の呼び出し元(他の場所にある複数のEuphoriaプログラム)へ即時に返されます。

様々なシグナルを扱うEuphoriaルーチンを指定するためにLinux/FreeBSDのsignal()関数にコールバックアドレスを渡すことができます(すなわちSIGTERM)。またqsort()などのC言語ルーチンを渡すことにより、Euphoria比較関数を指定することができます。