DelFusa Blog 総本山

プログラミングの話題とかです。

NEW | PAGE-SELECT | NEXT

≫ EDIT

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

| スポンサー広告 | --:-- | comments(-) | trackbacks(-) | TOP↑

≫ EDIT

クリスマス。PathCombine


。 ゜. 〇  o .゜  . ゜     . 。〇 .  ゜。 o .  o ゜   ゜   。 
l⌒l⌒l_______ __( \/ )__ _____  。  ゜   . ゜ . 。  o
|   | 。 | 0_)0_)V/ >   < | ! | 。 |⊂   。  ゜   . ゜ . 。. o  ゜. 。
|___l___|_l__|__|.|__| |_| (__/\_)_i_i_|_l__|⊃)   . 〇 ゜ . o  ゜. 。 ゜ 
。〇 .  ゜。 o .  o ゜    ☆     。 ゜  。 ゜   〇  ゜   。 
 . o  ゜. 。 .  .   。 o  .ミ彡 ∧,,∧     .  〇  ゜ .   ゜ o
  。.  . (ヽ,,,(ヽ  ゜    ゜ ミミミミ彡゚Д゚彡〇 ゜ 。    . ゜ . 。  o  
 . o   ミ   ミ゜ . o  ミミ ・彡彡  つ    o  .  .   o ゜. 〇 o
゜ .  o . ミ ∧,,∧    ミミ o 彡 彡 ミ  ゜ .  。  .   .   。  o 
 。  ゜   ミ,,゚Д゚彡  ミミ ミ ミ●彡彡 .   . 〇 ゜ . o  ゜. 。o  .
  . ゜ .  し  U  ミミ ∂ξ  彡O彡   ゜.    ゜    。.  。  〇.
   ∧,,∧     ∬  ミミ ◇ ,,彡彡彡♪   。  . ゜ .  o . .  . 
   ミ,,゚Д゚彡っ━~   ゙゙゙'''''--|i:ii|--'""  ∧,,∧ ♪.   . 。 .  .゜ . ゜ o
_と~,,  ~,,,ノ_.  ∀        |i:ii|  ♪ ミ゚Д゚,,彡    。 ゜   。.   .゜
    ミ,,,,/~), │ ┷┳━  [二二二]  ([lllll|]とミ ♪ ∧,,∧,,,,,,,,,,,,,,,
 ̄ ̄ ̄ .じ'J ̄ ̄| ┃    |   : ::::|    ミ  ミ~  ミ゚Д゚,,彡,,,,,,,,  ゙~
 ̄ ̄ ̄ ̄ ̄ ̄ ̄  ┻    L__」    U U     U U """"UU



さて、この記事は、Delphi アドベントカレンダーには載りませんです。

忙しくて、ネタが全然思いつきませんでした!!!


下記のP36、P37で紹介されている、Path.CombineをDelphiで実装しようと思いましたが.....

C#の素晴らしさ
http://www.slideshare.net/moririring/c-27141757


まあ、簡単なので、ちょっとネタとして弱いよね。

Delphiだって、この程度の機能なら一瞬で実装できちゃいます。

パラメーター引数つかって
さらさらっと、書くとこんなものになります。


function PathCombine(Values: array of String): String;
var
 I: Integer;
begin
 Result := '';
 for I := 0 to Length(Values) - 1 do
 begin
  Result := Result + IncludeLastPathDelim(ExcludeFirstStr(Values[I], '\'));
 end;
 Result := ExcludeLastPathDelim(Result);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin//
 ShowMessage(PathCombine(['C:\work', 'a.txt']));
 ShowMessage(PathCombine(['C:\work\', 'a.txt']));
 //結果 C:\work\a.txt

 ShowMessage(PathCombine(['C:\work\', 'bbb\a.txt']));
 ShowMessage(PathCombine(['C:\work\', 'bbb', 'a.txt']));
 //結果 C:\work\bbb\a.txt
end;

とはいっても、
さらさら、っと書けてるのは、DelFusaLibraryのおかげ。汎用関数群をもっていると結構便利なので、みなさんも自分独自のライブラリをどんどん構築していくと便利でいいと思いますよ。

IncludeLastPathDelim/ExcludeLastPathDelim/ExcludeFirstStr の関数についてはこのあたりを見てください。

文字列の前端後端に指定文字列を追加する関数
http://delfusa.main.jp/delfusafloor/technic/technic/002_IncludeFirstStr.html

DFLibrary/StringUnit/StringUnit.pas
http://delfusa.main.jp/delfusafloor/opensource/delfusalibrary/2013-01-31/DFLibrary/StringUnit/StringUnit.pas


改めて調べてみると、
DelphiのTPathは2つの値しか受け取らないので、ちょっとイマイチ。
使った事ないし、使う事もなさそうです。

C#のPathクラスCombineメソッドはどうやらスレッドセーフになっているそうです。さすが、C#...

Path.Combine メソッド (String, String) (System.IO)
http://msdn.microsoft.com/ja-jp/library/fyy7a5kt(v=vs.110).aspx

C#では、どうやって、Combineメソッドのような静的メソッドの内部をスレッドセーフにしているんだろうか.....
ParallelForを使っているような気がするんだけど、うむー。
DelphiでParallelForとか実装したい。難しそう!



とりあえず、このPathCombineのような機能をDelphiでスレッドセーフとして実装しておこうとすると、
どうやるんだろうか。と考えていたら思いつきました。

上記のPathCombine関数が、スレッドセーフにならないのは、
var I というローカル変数が問題になるからです。
変数「I」がスレッドAがループ処理しているときに
スレッドBが割り込んできて変数「I」の内容を書き換えてしまうので処理が正しく動作しなくなります。

これが、スレッドセーフではない。ってこと。


もちろん、上で使っているDelフサライブラリのIncludeやExclude関数もスレッドセーフじゃない。
スレッドセーフにするには、全部を書き換える必要があるわけです。

IncludeLastPathDelim/ExcludeLastPathDelim/ExcludeFirstStrと
それを使う、PathCombine、これらをStringHelperのメソッドとして実装すれば
メソッド内ローカル変数「I」は全てString毎に別の領域を使う変数として
動いてくれるので、スレッドで変数領域がぶつかる事がなくなるはずです。

そこまで考えて実装すれば、スレッドセーフにすることが出来るのでしょう。

単純な文字列変換系の機能に対して、スレッドセーフプログラミングを考えると
StringHelperはなくてはならない、非常に重要な機能になると思います。

これからの文字列処理を考える上で、
StringHelperを制するものはライブラリ全体を制するのかもしれません。

ただ、VCLやFireMonkeyもそうだし、DelFusaライブラリですら、
StringHelperで全部を実装し直してスレッドセーフ対応にするのは
なかなか骨の折れる作業の気がします。

手をつけるとしたら、1オリジン対応で機械的に変換するところからですね....

と、、、駄文を書いていたら、すっかり長文になって、1つのブログ記事ができあがっちゃった。

ま。関数一つ書いておいたから、まあ、よいとしましょうか。


書きたかったのは、次の記事なんですよね....

また、近いうちに書きますね。
スポンサーサイト

| 未分類 | 00:15 | comments:7 | trackbacks(-) | TOP↑

COMMENT

C#のPathCombineについて、

私の自分ライブラリだと、こう書きます。
var
f:TFileNameEx;
begin
f.FullPath:='c:work';
f.AddPath('bbb');
f.AddPath('ccc');
f.Name:=a.txt';
//結果 c:bbbccca.txt
Edit1Text:=f.FullName;

今更なオブジェクト型です。
オブジェクトやメソッドなどの定義名にセンスがないので
我ながら使う度に恥かしくなります。^^
スレッドセーフ、私のスキルではよくわからないです・・・
BeginThread関数を使う場合に問題になるものでしょうか?
古いDelphiだと関係ないのかな

| matsui | 2013/12/29 04:23 | URL | ≫ EDIT

メッセージ、ありがとうございます。

自分のライブラリっていいですよね。

特に変だとは思いませんが
今時は命名は変だと思ったらすぐにどんどん変えまくる開発スタイルが流行な気がします。DelphiでもVisualStudioにも最近はリファクタリング機能が付いているので、大変便利ですよ。

オブジェクト型の方がやばいかも。
最近のDelphiはrecordがメソッドを持てるので、そちらが宣言のみで使えるので、使い勝手が同じになります。

スレッドセーフなのは、スレッドAとスレッドBが
「共通関数」を使う場合にローカル変数「I」を同時書き込みするようなものだとだめで、メモリ領域が別だと大丈夫だったりします。Delphiではあまり使わないというのは確かなのですが、
慣れれば簡単な概念ですよ。

スレッドセーフプログラミングが一般的になってきたら、自分のライブラリをスレッドセーフに対応していくのもきっと楽しめると思います。

| Delフサ | 2013/12/30 23:11 | URL |

明けましておめでとう御座います。

スレッドセーフについて
教えて頂きありがとう御座いました。やっと理解できました。
無意識に「ライブラリ=クラス型のコード」と決めてしまい、何が問題なの?と思っていました。
確かに共通関数を同時にアクセスすると問題になりそうですね。

オブジェクト型について
メソッドがあるrecordについて調べました。delphi7で対応しているとか。
残念ながら、使っているdelphiは古くて対応していませんでした。
オブジェクト型と何が違うか、検索するとこちらのブログ記事を見つけました。
「Methodが実装できるrecord型、それはもうダークサイドで。」
http://delfusa.blog65.fc2.com/blog-entry-109.html
オブジェクト型は、フォースの暗黒面ですか・・・たしかにそうかも。

オブジェクト型は、よく使う文字列とファイル名処理の2つのコードでしか使用していません。
最近は、オブジェクト型の不便さ(継承出来ない等)で、クラス型への変更を考えてます。
その前に、try finally 、生成、破棄を自動追加するような支援ツールが完成したらの話ですが・・・。

| matsui | 2014/01/01 15:42 | URL | ≫ EDIT

こんにちは。
あけましておめでとうございます。

オブジェクト型のなつかしい記事でしたね。
そう。object型で事足りる事が新record型になってしまったんですよね。
謎ですよ。もう。全く。

最初から非推奨という事をいっていなければよかったのに
と思わざるおえませんね。

通常のクラスが宣言と同時に生成するような事ができればいいのにな。とか、
任意位置で宣言できたりしたらいいのにな、と思います。

Delphiには言語構文のセンスを磨いて進化していって欲しいです。

| Delフサ | 2014/01/03 02:12 | URL | ≫ EDIT

PathCombine関数とスレッドセーフ

こんにちは。紹介のあったPathCombine関数ですが、
自分の今までの思い込みだと、ローカル変数は別スタック上に確保されるので、
十分にスレッドセーフではないかなぁっと思っていました。
念のため、久しぶりにDelphiを起動してみて、簡単な実験してみたのですが、
アドレスが別になっていて、大丈夫そうに見えました。
下のような関数を複数のスレッドから呼んで、ループ中のIの値と戻り値で確認しました。

function TestFunc(num: Integer): Integer;
var
I: Integer;
Start: Integer;
Goal: Integer;
begin
Start := num;
Goal := num + 10;

for I := Start to Goal do
begin
Sleep(1000);
//OutputDebugString(PChar(IntToStr(Integer(@I)) + ':' + IntToStr(I)));
result := I;
end;
end;

たまたま大丈夫だったのか、条件によってなのか、
幸いに自分の思い込みが正しかったのか…、
気になりましてコメントしました。
Delphiはかなり久しぶりで、コードはどうぞご容赦を…。

| Delphi久しぶり | 2014/01/07 02:04 | URL |

おおお。そうなんですか!?

VB.NETでスレッド関係の処理をかなりやった時に、問題になったのがそこだったので、そう思ってたのですが

おっしゃる通りだとしたら、Delphiの関数はスレッドに対して強いものですね。別スタック上に確保されるのか....

ちょっと、私も実際にスレッドから呼び出して試してみたいと思います。

スレッド周りのデバッグって、ほんとに、たまたまOKな場合もあってきついんですよね。

情報ありがとうございます。すごく助かります。

| Delフサ | 2014/01/12 18:02 | URL |

そうなのですね…。

VB.NETでも同じような関数を作って、複数スレッドから呼び出してみたのですが、
ループ変数の値と戻り値デバッグ出力でで見てみたところ、大丈夫そうでした。
シンプルな処理なので、たまたま大丈夫だっただけかもしれません。
変数のアドレスについては、VB.NETで見る方法が分からず、確認できませんでした。

スレッド関連の処理はシンプルなのを数本やったことが程度なので、
本当に、あまりあてにならない情報と思ってもらえたら、と思います。

| Delphi久しぶり | 2014/01/14 00:37 | URL |















非公開コメント

PREV | PAGE-SELECT | NEXT

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。