2013-06-16
■[Delphi]DelphiでDLLとdylibを動的にロードする
DelphiからDLLを動的にロードする場合、WindowsではLoadLibraryとGetProcAddressを使っていましたが、MacOSXではどうすればいいのか調べてました。
MacOSX(Posix)だとdlopen, dlsymを使うことになるらしいです。
dlopen(3) Mac OS X Developer Tools Manual Page
dlsym(3) Mac OS X Developer Tools Manual Page
System.SysUtilsのコードを見てみたら、どうやら最近のDelphiのバージョンでは、MacOSXでもLoadLibraryとGetProcAddressからこれらを使うようラップしているようでした。
IFDEFがたくさん書かれていて読みづらいですが、SafeLoadLibraryという関数がSysUtilsにあり、これはWindows, MacOSX両方で使用可能のようです。
System.SysUtils.SafeLoadLibrary - RAD Studio API Documentation
ただし、GetProcAddressについてはXE4の段階ではWindowsだとSysUtilsのほうには定義がなく、WinApi.Windows経由を使うことになりそうです。
また、dylibを作成する際MacOSXの場合は、exportsはユニット側に書き、アンダースコアを先頭に付けた名前でエクスポートしないと認識されないそうです。
クロスプラットフォームの共有ライブラリ - RAD Studio
この情報を基に、WindowsではDLLを、MacOSXではdylibを動的にロードするようなコードを書いてみます。
試したバージョンはDelphi XE4です。
DLL/dylib側
mylib.dpr
LIBPREFIXに空文字列を指定しているのは、dccosxだとデフォルトが"lib"になっていたためです。
library mylib; uses MyLibrary; {$LIBPREFIX ''} begin end.
MyLibrary.pas
足し算の結果を返すAdd関数と、メッセージボックスでテキストを表示するSay手続きをエクスポートしています。
呼び出し規約は他の言語からも使いやすいように、とりえあえずcdeclを指定しています。
Say関数では引数の文字列はUTF8エンコードされたバイト列のポインタを前提にしています。
unit MyLibrary; interface uses FMX.Dialogs; function Add(X, Y: Integer): Integer; cdecl; procedure Say(Text: PAnsiChar); cdecl; exports {$IFDEF MACOS} Add name '_Add', Say name '_Say' {$ELSE} Add, Say {$ENDIF} ; implementation function Add(X, Y: Integer): Integer; cdecl; begin Result := X + Y; end; procedure Say(Text: PAnsiChar); cdecl; begin ShowMessage(UTF8ToString(Text)); end; end.
動的にライブラリをロードするコード
dynamic_loading.dpr
コンソールアプリケーションで、SafeLoadLibraryを使ってライブラリをロードし、Add関数、Say手続きを呼び出しています。
MacOSの場合はmylib.dylib、Windowsの場合はmylib.dllを使います。
ファイルの文字コードはUTF8です(日本語を含むので)。
program dynamic_loading; {$APPTYPE CONSOLE} uses System.SysUtils {$IFDEF MSWINDOWS} , WinApi.Windows {$ENDIF} ; const {$IFDEF MacOS} LIB_NAME = 'mylib.dylib'; {$ELSE} LIB_NAME = 'mylib.dll'; {$ENDIF} type TAdd = function(X, Y: Integer): Integer; cdecl; TSay = procedure(Text: PAnsiChar); cdecl; var Handle: THandle; Add: TAdd; Say: TSay; begin Handle := SafeLoadLibrary(LIB_NAME); try if Handle <> 0 then begin @Add := GetProcAddress(Handle, 'Add'); if @Add <> nil then Writeln(Add(10, 20)); @Say := GetProcAddress(Handle, 'Say'); if @Say <> nil then Say('こんにちは'); end; finally FreeLibrary(Handle); end; end.
実行結果
Windows7(32bit)
MacOSX 10.6
ソースコード
2013-05-23
■[Delphi]Delphiのレコードヘルパを試す
レコードヘルパはInteger型やString型などにメソッドを追加する機能。
試したのはDelphiXE4。2010以降の機能なのかな。
test_my_record_helper.dpr
program test_my_record_helper; {$APPTYPE CONSOLE} type TMyIntegerHelper = record helper for Integer function Add(X: Integer): Integer; end; function TMyIntegerHelper.Add(X: Integer): Integer; begin Result := Self + X; end; var Value: Integer; begin Value := 10; Writeln(Value.Add(20)); end.
実行結果
>dcc32 test_my_record_helper.dpr >test_my_record_helper.exe 30
参考
2013-05-15
■[Delphi]Delphi XE4での変更点を試す
Delphi XE4が先日発売されましたね。私はiOSアドオンなしでXE3からアップグレードしました。
XE3を購入したときのポイントが余ってたおかげで、SEshopで1,200円程度で購入できました。
iOS対応を除くと、機能追加はほとんどないのですが、リリースノートに載っているインデックスが0から始まるの文字列と、追加されたTIntegerHelperを試してみました。
Delphi XE4 および C++Builder XE4 の新機能 - RAD Studio
test_zerobased_str.dpr
ZEROBASEDSTRINGSにONを指定するとString型のインデックスは0から始まります。
program test_zerobased_str; {$APPTYPE CONSOLE} {$ZEROBASEDSTRINGS ON} var Foo: String; I: Integer; begin Foo := 'ABCDE'; for I := 0 to Length(Foo) - 1 do Writeln(Foo[I]); end.
実行結果
>dcc32 test_zerobased_str.dpr >test_zerobased_str.exe A B C D E
ちなみに、OFFの場合はこうなります。
>test_zerobased_str.exe A B C D
test_integer_helper.dpr
program test_integer_helper; {$APPTYPE CONSOLE} (* System.SysUtilsをusesに追加することでヘルパークラスが有効になる *) uses System.SysUtils; var IntValue, IntValue2: Integer; StrValue: String; begin IntValue := 12345; (* IntegerからStringへ変換 *) StrValue := IntValue.ToString; Writeln(StrValue); (* StringからIntegerへ変換 *) IntValue2 := Integer.Parse('54321'); Writeln(IntValue2); end.
実行結果
>dcc32 test_integer_helper.dpr >test_integer_helper.exe 12345 54321
参考
2013-05-11
■[nginx]nginxのリバースプロキシでキャッシュする
メモ。nginxのリバースプロキシでファイルをキャッシュする方法。
設定したページはこれ。バックエンドはGoogleAppEngine。
試したnginxのバージョンは0.7。Ubuntu10.04のなので古い。
nginxの設定
nginx.confの中でconf.dやsites-enabledがincludeされるようになってることが前提。
/etc/nginx/conf.d/proxy_cache.conf
proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=my-key:16m max_size=100m inactive=120m; proxy_temp_path /var/cache/nginx/tmp;
httpディレクティブにproxy_cache_pathを設定する。levelsは、キャッシュファイルをサブディレクトリで保存する設定。
この例の場合、my-keyというキーの領域で共有メモリは16MB使用、キャッシュ全体のサイズは100MB、120分間アクセスが無ければキャッシュは削除される。
proxy_temp_pathで一時ディレクトリを設定する。対象のディレクトリはあらかじめ作成し、nginxのworkerプロセスがアクセスできるようにしておく。
/etc/nginx/sites-available/my-site
server {
listen 80;
server_name www.example.com;
proxy_set_header X-Real-IP $remote_addr;
access_log /var/log/nginx/my-site.access.log;
error_log /var/log/nginx/my-site.error.log;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_ignore_headers Cache-Control;
proxy_cache my-key;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 10m;
}
}
サイトごとの設定では、proxy_cacheでkeys_zoneに指定した名前、proxy_cache_validでキャッシュ対象と有効な時間を指定する。
この例では、レスポンスのステータス番号が200と302の場合は60分、404の場合は10分としている。
リバースプロキシ先のレスポンスにCache-Controlヘッダがついていて、"no-cache"とされているとキャッシュされないので、その場合はproxy_ignore_headersで無効化する。
nginxの設定をリロードする
$ sudo /etc/init.d/nginx reload
参考
2013-05-10
■[bash]bashのexecを試す
bashスクリプトでexecを使うと、シェルが実行したコマンドに置き換えられる。
Pythonのスクリプトをシェルスクリプトから実行することが結構あって、プロセスの監視云々とか考えるとexec使ったらいいんじゃないとかどっかで聞いた気がする。
execを使わない場合
以下のようなPythonスクリプトをbashスクリプト経由で実行してみる。
main.py
# coding: utf-8 import time def main(): while True: time.sleep(1) if __name__ == '__main__': main()
run
#!/bin/bash
python main.py
実行結果
$ ./run ^Z [1]+ Stopped ./run $ ps aux (中略) tokibito 30303 0.0 0.1 12312 1160 pts/1 T 23:42 0:00 /bin/bash ./run tokibito 30304 0.5 0.5 32172 5304 pts/1 T 23:42 0:00 python main.py
execを使う場合
run
#!/bin/bash exec python main.py
実行結果
$ ./run ^Z [1]+ Stopped ./run $ ps aux (中略) tokibito 30348 0.1 0.5 32172 5308 pts/1 T 23:47 0:00 python main.py
execを使わない場合は2プロセスになってしまうけど、execを使うと1プロセスで済む。


