tag:blogger.com,1999:blog-73707960007769204362024-02-21T10:11:57.259+09:00佐祐理ブログ.NETとWin32ネタが主です。UNIXもいじるのでその辺りも混じるかも。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.comBlogger101125tag:blogger.com,1999:blog-7370796000776920436.post-26999169312265063082018-04-11T11:48:00.000+09:002018-04-11T11:48:30.701+09:00MUIによるアプリケーションの国際化ffftpで使われている技術、第一弾はMUIによるアプリケーションの国際化です。<br />
Windowsではリソースファイルによる国際化が提供されていました。しかし、リソースファイルには単に言語情報が付与されているだけで切り替え機能が提供されていないため、言語切り替えはアプリケーションの責任となっていました。<br />
しかし、Windows Vistaから<a href="https://msdn.microsoft.com/en-us/library/dd319073(v=vs.85).aspx">MUI; Multilingual User Interface</a>というリソース切り替え機構が導入されました。MUIを適切に構成すると、<a href="https://msdn.microsoft.com/en-us/library/ms684175(v=vs.85).aspx">LoadLibrary</a>でリソースを読み込むだけで適切な言語のリソースを開くことができます。またMUI未対応環境のために互換APIとして<a href="https://msdn.microsoft.com/en-us/library/dd318709(v=vs.85).aspx">LoadMUILibrary</a>が提供されています。<br />
<br />
ffftpでは <a href="https://github.com/sayurin/ffftp/blob/v3.0/main.cpp#L278-L287">github:sayurin/ffftp/blob/v3.0/main.cpp#L278-L287</a> のようにWindows Vista未満を対象とするビルドに対しては、hInstanceをLoadMUILibraryで得られたリソースハンドルに置き換えています。Windows Vista以降を対象とする場合、そもそもWinMainで受け取るhInstanceはリソース切り替え済みのハンドルとなっているためコードは一切必要ありません。<br />
なお、LoadMUILibraryはmuiload.h及びmuiload.libで提供されるため静的リンクしています。ここでmuiload.libはVisual C++ 2013以前を対象にビルドされているため<a href="https://docs.microsoft.com/ja-jp/cpp/porting/overview-of-potential-upgrade-issues-visual-cpp#errors-involving-crt-functions">Visaul C++ 2015以降で使用するためにはlegacy_stdio_definitions.libも併せてリンク</a>する必要があります。<br />
<br />
リソースは通常RC; Resource Compilerを使用してコンパイルしますし、Visual Studioではビルドの一環として組み込まれています。しかしMUI対応リソースは<a href="https://msdn.microsoft.com/en-us/library/dd319113(v=vs.85).aspx">MUIRCT</a>を使ってコンパイルする必要があり、Visual Studioのビルド手順には組み込まれていません。<br />
そこでffftpでは<a href="https://github.com/sayurin/ffftp/blob/v3.0/MuiResourceCompile.targets">MuiResourceCompile.targets</a>を用意しました。単にビルド手順に含めるだけでなく次のようにプロパティページも表示することができます。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIsUUgUaUt5qYfn1b1T8sYl_O51G19tDYCTa7Z5pOntssflMI9f0ZjwXh0HGkJALHX0ZFmTuGAafpMmOPdIcIB74_TpCLxDwMhrFYxPNJdG9cCUsF2lzmwbFBj5yMn8m_Lo2Tr-zMMlojI/s1600/MuiResourceProperty.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="558" data-original-width="822" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIsUUgUaUt5qYfn1b1T8sYl_O51G19tDYCTa7Z5pOntssflMI9f0ZjwXh0HGkJALHX0ZFmTuGAafpMmOPdIcIB74_TpCLxDwMhrFYxPNJdG9cCUsF2lzmwbFBj5yMn8m_Lo2Tr-zMMlojI/s320/MuiResourceProperty.png" width="320" /></a></div>
<br />
(まだ改良の余地はありますが、それはそれ…。)<br />
ともあれ、これでビルドを行うと<br />
<ul>
<li>ffftp.exe</li>
<li>en-US\ffftp.exe.mui</li>
<li>ja-JP\ffftp.exe.mui</li>
</ul>
<div>
が得られます。これらを配置することでMUIによるリソース切り替えが実現できます。</div>
佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-17923006013710732092018-04-11T11:37:00.000+09:002018-04-11T11:37:14.018+09:00ffftpのメンテナンスを引き継ぎましたffftpのメンテナンスを引き継ぎました。<a href="https://github.com/sayurin/ffftp" target="_blank">github:sayurin/ffftp</a>で公開しています。メンテナンス方針としてはGUIを大きく変えることはしません。最新のVisual Studioを使用し、動作環境をWindows XP以降としています。この環境で利用できるC++17やWindows APIを使用してコードをリファクタリングしていきます。また外部ライブラリの利用を排し、Windowsが提供する機能を使用します。これによりセキュリティアップデートをWindows Updateに任せることができます。<br />
不具合を見つけた際には <a href="https://github.com/sayurin/ffftp/issues">github:sayurin/ffftp/issues</a> まで報告いただけたら幸いです。<br />
<br />
このブログでは、ffftpで使用しているC++機能やWindows APIなどを順次紹介していきたいと思います。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-70446095727734795482017-02-05T22:19:00.000+09:002017-02-05T22:19:56.506+09:00cross browserでのタッチ操作Webサイトをタッチ操作に対応させようとしたところ、非常に苦労したため、得られた成果をまとめておこうと思います。<br />
目指すゴールは<b>手持ちのデバイスでドラッグ・スライドを実現する</b>ことです。Webでは変化が目まぐるしいのでこの記事が対象とする現時点での環境を挙げておきます。<br />
<ul>
<li>SurfacePro / Windows 10 / mouse, touch, pen</li>
<ul>
<li>Internet Explorer 11</li>
<li>Edge 14</li>
<li>Chrome 56</li>
<li>Firefox 51</li>
</ul>
<li>MacBook Air / OS X / mouse</li>
<ul>
<li>Safari</li>
<li>Chrome 56</li>
</ul>
<li>iPhone SE / iOS 10.2 / touch</li>
<ul>
<li>Safari</li>
<ul>
</ul>
</ul>
<li>Xperia / Android 4.1.2 / touch, mouse</li>
<ul>
<li>Chrome</li>
<li>Firefox</li>
</ul>
<li>Fire HD 8 / Fire OS 5.3.2.1 / touch, mouse</li>
<ul>
<li>Chrome</li>
<li>Firefox</li>
</ul>
</ul>
<hr />
<div>
ブラウザーは従来より<a href="https://www.w3.org/TR/uievents/#events-mouseevents">Mouse Events</a>を提供していましたが、最近はmouse・touch・pen等を統一的に扱える<a href="https://www.w3.org/TR/pointerevents/">Pointer Events</a>が提供されています。ただし<a href="http://caniuse.com/#feat=pointer">対応状況</a>としてはIE11 / Edge / Chromeが対応、Firefox / Safariが未対応のようです。これとは別に<a href="https://www.w3.org/TR/touch-events/">Touch Events</a>が提供されていますが、<a href="http://caniuse.com/#feat=touch">こちら</a>はChrome / iOS / Androidが対応、IE11 / Edge / Firefoxが未対応のようです。総合すると<br />
<ol>
<li>Pointer Eventsに対応ならPointer Eventsのみ</li>
<li>Pointer Eventsに未対応でTouch Eventsに対応ならTouch EventsとMouse Eventsの両方</li>
<li>Pointer EventsとTouch Eventsに未対応ならMouse Eventsのみ</li>
</ol>
<div>
と分岐しつつ複数を併用する必要がありそうです。</div>
<div>
<br /></div>
<div>
さてイベントの登録・削除は従来はIEに制限がありましたが<a href="https://msdn.microsoft.com/en-us/library/ff975245(v=vs.85).aspx">IE9以降</a>はaddEventListenerのみで実現できます。しばしば第3引数useCaptureにfalseが渡されますが、<a href="https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener#Browser_compatibility">対応していなかったのはFirefox 6未満</a>のようですので省略して構わないでしょう。<br />
<br />
タッチ操作の場合、ページのスクロールなどブラウザー側の処理と競合します。この問題については<a href="https://www.w3.org/TR/pointerevents/#the-touch-action-css-property">touch-action</a>というCSSプロパティが用意されています。<a href="http://caniuse.com/#feat=css-touch-action">対応状況</a>としてはIE11 / Edge / Chromeが対応、Firefoxが次バージョンで対応、Safariが一部のみ対応ということで、これとは別の対策も必要です。実のところ<a href="https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW24">SafariではpreventDefault()メソッドを使って抑止する</a>ことになっています。<br />
<br />
ブラウザー側の処理にはこれ以外もあり、Pointer EventsやTouch Eventsが処理されなかった場合は互換のためにMouse Eventsが呼ばれることになっています。これらについてもpreventDefault()メソッドで抑止できることになっています。</div>
<hr />
<div>
以上を踏まえて書いたコードがこちら</div>
<script src="https://gist.github.com/sayurin/ce5c2e235b381351756a9635562d8851.js"></script></div>
佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-2011396992775541472016-08-21T22:34:00.001+09:002016-08-21T22:34:53.797+09:00DLLで関数のexport<p>今更な話題かもしれませんが、あまり知られていないと思うので記事にしておきます。サンプルコードとして</p>
<blockquote class="code">#include <Windows.h>
BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) {
return TRUE;
}
extern "C" int __stdcall Add(int a, int b) {
return a + b;
}</blockquote>
<p>でAdd関数をエクスポートする場合を考えます。</p>
<p>DLLで関数をエクスポートする方法の中でもっともよく使われる方法は<a href="https://msdn.microsoft.com/ja-jp/library/28d6s79h.aspx">モジュール定義(.def)ファイル</a>です。</p>
<blockquote class="code">LIBRARY
EXPORTS
Add</blockquote>
<p>で作成したファイルを<a href="https://msdn.microsoft.com/ja-jp/library/34c30xs1.aspx">リンカーオプション/DEF</a>で指定します。<br/>
この方法の問題点は、定義が分かれてしまい、ソースコードからはどの関数がエクスポートされるか判別できないことです。</p>
<p>そこでソースコード内にエクスポートするかどうかを埋め込む方法が提供されています。<a href="https://msdn.microsoft.com/ja-jp/library/3y1sfaz2.aspx">__declspec(dllexport)</a>を指定する方法です。</p>
<blockquote class="code">extern "C" __declspec(dllexport) int __stdcall Add(int a, int b) {
return a + b;
}</blockquote>
<p>__declspec(dllexport)をextern "C"の後~__stdcallの前の辺りに挿入します。<br/>
この方法の問題点は、x86においてエクスポートされる関数名が_Add@8(装飾名)となってしまうことです。これは呼び出し規約において呼び出し先関数が引数のスタックを復元するため、復元するサイズを呼び出し元に通知する必要があるからです。</p>
<p>そこでこの問題を解消する別の解決策を模索します。</p>
<ul><li><a href="https://msdn.microsoft.com/ja-jp/library/hyx1zcd3.aspx">モジュール定義ファイル</a>においてはエクスポートする関数名を明示することができます。</li><li><a href="https://msdn.microsoft.com/ja-jp/library/7k30y2k5.aspx">リンカーオプション/EXPORT</a>はモジュール定義ファイルとほぼ同様の書式で、ドキュメントにはありませんが同じ書式を使用してエクスポートする関数名を明示できます。</li><li><a href="https://msdn.microsoft.com/ja-JP/library/7f0aews7.aspx">#pragma comment</a>を使用するとリンカーオプション/EXPORTをソースコード内で指定できます。</li><li><a href="https://msdn.microsoft.com/ja-jp/library/b0084kay.aspx">定義済みマクロ__FUNCTION__と__FUNCDNAME__</a>を使用すると関数名と装飾名が得られます。</li></ul>
<p>以上を総合すると</p>
<blockquote class="code">extern "C" int __stdcall Add(int a, int b) {
#pragma comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
return a + b;
}</blockquote>
<p>これでモジュール定義ファイルを用いることなく、ソースコード内での記述で、関数をエクスポートすることができまし…たんですが、IntelliSenseが<i>expected a ')'</i>と警告します。余計なお世話ですがこれも解消しましょう。</p>
<ul><li>#pragmaはマクロ内で使えないが__pragma()であればマクロで使える。</li><li>IntelliSenseは__EDG__を定義するがコンパイラーは定義しないのでコード分岐できる。</li></ul>
<p>以上を踏まえて最終的に</p>
<blockquote class="code">// ヘッダーなどに
#ifdef __EDG__
#define DLLEXPORT
#else
#define DLLEXPORT __pragma(comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__))
#endif
// 関数内に
extern "C" int __stdcall Add(int a, int b) {
DLLEXPORT;
return a + b;
}</blockquote>
<p>と書くことでスマートに記述できるようになりました。</p>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-42654350419877920672016-07-09T22:12:00.002+09:002016-07-09T22:13:30.034+09:00Visual C++ CoroutineとBoost Coroutine<a href="http://sayurin.blogspot.com/2008/05/boostasio.html">以前にBoost.Asioを使ってみた</a>んですが、その時は非同期機能を使っていませんでした。非同期機能の場合、完了のコールバックを処理する必要がありますが、そのコーディングはどうしても煩雑になってしまいます。<br />
この点に関してC#言語ではVS2013でasync機能によるサポートが行われるようになりました。この機能は非同期呼び出し時のコンテキストをコンパイラーが保持しておき、完了コールバック時にコンテキストを復元することで一続きの関数のように処理するものです。この機能は一般的にはCoroutine; コルーチンと呼ばれるようです。<br />
<br />
実はC++言語でもコルーチンを標準に導入するべく検討がされているらしく、VS2015からサポートされています。VS2015 Update1からはプラットフォームを選ばず機能提供されています。それとは別にBoostライブラリでもBoost.Coroutineによるコルーチンが提供されています。こちらはコンパイラー側のサポートなしにアセンブラでスタックを強引に書き換えることで実現されているらしいです(詳しくはわかりません)。その影響でWindowsプラットフォームではBoost.Coroutineを使うためには/SAFESEH:NOオプションを付け安全な例外ハンドラーが存在しない旨を宣言する必要があります。<br />
<br />
そこで、C++言語ネイティブなコルーチンが提供される環境ではそちらを、提供されない環境ではBoost.Coroutineに切り替え、ソースコードを共通化できるライブラリを用意してみました。<br />
<br />
<script src="https://gist.github.com/sayurin/7232bdc946c5c3794df880ffd4057e39.js?file=coroutine.h"></script>
<br />
これを使うとcoroutine::result<Result> func(..., coroutine::handler handler)のシグネチャを持つ関数を呼び出すことができます。サンプルはこちら。<br />
<br />
<script src="https://gist.github.com/sayurin/7232bdc946c5c3794df880ffd4057e39.js?file=sample.cpp"></script>
佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-39267177950446809412016-04-19T22:26:00.000+09:002016-04-19T22:26:21.452+09:00Windowsの各種Timerの精度についてWindowsはいくつかのTimerを提供しています。<br />
<ul>
<li><a href="https://msdn.microsoft.com/en-us/library/dd743609(v=vs.85).aspx" target="_blank">Multimedia Timer</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/ms686796(v=vs.85).aspx" target="_blank">Timer Queue</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/ms686766(v=vs.85).aspx" target="_blank">Threadpool Timer</a></li>
<li>同期オブジェクトを使用する<a href="https://msdn.microsoft.com/en-us/library/ms687012(v=vs.85).aspx" target="_blank">Waitable Timer</a></li>
<li>メッセージループを使用する<a href="https://msdn.microsoft.com/en-us/library/ms632592(v=vs.85).aspx" target="_blank">Message Timer</a></li>
</ul>
<div>
があり、それぞれに特徴があります。</div>
<div>
<ul>
<li>Message Timerは分解能が16msなので精度はよくない。</li>
<li><a href="https://msdn.microsoft.com/en-us/library/dd757634(v=vs.85).aspx" target="_blank">Multimedia Timerは廃止済みでTimer Queueを推奨</a>している。</li>
<li><a href="https://msdn.microsoft.com/en-us/library/dd757633(v=vs.85).aspx" target="_blank">Multimedia TimerはtimeBeginPeriodを呼ぶことで精度が向上</a>する。</li>
</ul>
<div>
などはよく知られていると思います。しかし実際どうなっているのか気になって調べたところ<a href="http://omeg.pl/blog/2011/11/on-winapi-timers-and-their-resolution/" target="_blank">On WinAPI timers and their resolution</a>という比較記事を見つけました。しかしOSバージョンが明示されていないなど疑問は解消しなかったため、自分で比較してみることにしました。</div>
</div>
<div>
<a name='more'></a></div>
<div>
比較コードおよびWindows 7、Windows 8.1、Windows 10での測定結果を<a href="https://gist.github.com/sayurin/f47ddf0db901c1e0c5a905c718a749d1" target="_blank">gist</a>にまとめました。その結果、いろいろなことがわかりました。</div>
<div>
<ul>
<li>Message Timerは依然として分解能16msのままである。</li>
<li>Multimedia TimerはtimeBeginPeriodを呼ばなくても精度がよい。</li>
<li>むしろTimer Queues、Threadpool Timer、Waitable TimerこそtimeBeginPeriodの影響を受けている。</li>
<li>Timer QueuesとThreadpool TimerはWindows 7ではMultimedia Timerに依存していたようで精度がよかったが、Windows 8以降でtimeBeginPeriodを呼ばない場合はMessage Timer相当に精度が下がる。更にWindows 10ではtimeBeginPeriodを呼んだ場合でも多少精度が下がる。</li>
<li>Waitable TimerもtimeBeginPeriodを呼ばない場合はMessage Timer相当に精度が下がる。</li>
</ul>
<div>
結論としては、Windows 10時代においても<b>Multimedia Timer安定</b>でしょうか。</div>
</div>
佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-39492434745937451682016-02-02T22:50:00.000+09:002016-02-02T22:53:50.327+09:00C++からWindows APIを呼び易くするWindows APIの多くはC言語を前提としています。次のように戻り値がHRESULTなどのエラーコードとなり、真の戻り値は関数の最後の引数にポインターとして返される構造をしているものが多々あります。
<blockquote class="code">HRESULT Direct3DCreate9Ex(
UINT SDKVersion,
IDirect3D9EX **ppD3D
);</blockquote>
これをC++言語から扱いやすくしたいと思います。<br />
一般的には次のようなcheck()関数で異常値については例外を投げることになるでしょう。
<blockquote class="code">void check(HRESULT hr){
if(FAILED(hr))
throw hr;
}</blockquote>
本題は真の戻り値です。<br />
API関数には任意の引数があるため最後の引数を扱うのは困難です。幸いC++言語には可変長テンプレート引数があり、それをうまく扱うstd::tupleクラスとstd::tuple_elementクラスがあります。次のようなlastクラスを定義できます。
<blockquote class="code">template<class... Args>
struct last : std::tuple_element<sizeof...(Args)-1, std::tuple<Args...>> {};</blockquote>
これは例えば int, double, std::string のような型リストがあった場合にまずはstd::tuple<int, double, std::string>型を作り、これに対して最後の型を取り出します。その結果、last<int, double, std::string>::typeはstd::stringに展開されます。<br/>
ここまでくれば関数については簡単に書けるかもしれません。テンプレートと特殊化を使います。
<blockquote class="code">template<class Func>
struct last_argument;
template<class Ret, class... Args>
struct last_argument<Ret(Args...)> : last<Args...> {};</blockquote>
これで例えば last_argument<Direct3DCreate9Ex>::typeはlast<UINT, IDirect3D9EX**>::typeに展開され最終的にIDirect3D9EX**が得られます。<br/>
ところが、Visual C++には様々な呼び出し規約があるため、このままではデフォルトの呼び出し規約の関数にしか対応できていません。<br/>
幸いVisual C++自身もこの問題に直面していてこれを解決するマクロを使用しているため、ここではそれを流用します。ついでにメンバー関数にも対応しておきます。
<blockquote class="code">template<class Func>
struct last_argument;
#define LAST_ARGUMENT(CALL_OPT) \
template<class Ret, class... Args> struct last_argument<Ret CALL_OPT(Args...)> : last<Args...> {}; \
template<class Ret, class... Args> struct last_argument<Ret (CALL_OPT*)(Args...)> : last<Args...> {};
_NON_MEMBER_CALL(LAST_ARGUMENT)
#undef LAST_ARGUMENT
#define LAST_ARGUMENT(CALL_OPT, CV_OPT, REF_OPT) \
template<class Class, class Ret, class... Args> struct last_argument<Ret(CALL_OPT Class::*)(Args...) CV_OPT REF_OPT> : last<Args...> {};
_MEMBER_CALL_CV_REF(LAST_ARGUMENT)
#undef LAST_ARGUMENT
</blockquote>
さてこれらを使った関数を用意します。std::invoke()を使うと関数呼び出しが簡単に表現できます。
<blockquote class="code">template<class Func, class... Args, class Result = std::remove_pointer_t<last_argument<Func>::type>>
auto get(Func func, Args&&... args) {
Result result;
check(std::invoke(func, std::forward<Args>(args)..., &result));
return result;
}
#define GET(OBJECT, METHOD, ...) get(&std::remove_reference_t<decltype(OBJECT)>::METHOD, OBJECT, __VA_ARGS__)
</blockquote>
以上を使うと<blockquote class="badcode">HRESULT hr;
IDirect3D9EX* d3d;
hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d);
if (FAILED(hr))
throw hr;
IDirect3DDevice9Ex* d3device;
hr = d3d->CreateDeviceEx(引数, いろ, いろ, &d3device);
if (FAILED(hr))
throw hr;
</blockquote>と書いていたものが
<blockquote class="code">auto d3d = get(Direct3DCreate9Ex, D3D_SDK_VERSION);
auto d3device = GET(d3d, CreateDeviceEx, 引数, いろ, いろ);</blockquote>
と書けるようになりました。
<hr/>
改めてまとめると
<blockquote class="code">void check(HRESULT hr){
if(FAILED(hr))
throw hr;
}
namespace details {
template<class... Args>
struct last : std::tuple_element<sizeof...(Args)-1, std::tuple<Args...>> {};
template<class Func>
struct last_argument;
#define LAST_ARGUMENT(CALL_OPT) \
template<class Ret, class... Args> struct last_argument<Ret CALL_OPT(Args...)> : last<Args...> {}; \
template<class Ret, class... Args> struct last_argument<Ret (CALL_OPT*)(Args...)> : last<Args...> {};
_NON_MEMBER_CALL(LAST_ARGUMENT)
#undef LAST_ARGUMENT
#define LAST_ARGUMENT(CALL_OPT, CV_OPT, REF_OPT) \
template<class Class, class Ret, class... Args> struct last_argument<Ret(CALL_OPT Class::*)(Args...) CV_OPT REF_OPT> : last<Args...> {};
_MEMBER_CALL_CV_REF(LAST_ARGUMENT)
#undef LAST_ARGUMENT
}
template<class Func, class... Args, class Result = std::remove_pointer_t<details::last_argument<Func>::type>>
auto get(Func func, Args&&... args) {
Result result;
check(std::invoke(func, std::forward<Args>(args)..., &result));
return result;
}
#define GET(OBJECT, METHOD, ...) get(&std::remove_reference_t<decltype(OBJECT)>::METHOD, OBJECT, __VA_ARGS__)
</blockquote>
このコードはVisual Studio 2015 Update1のC++コンパイラーにて正常動作するとともにIDEのIntelliSenseでも正しく解釈されることを確認しています。
ただし、x64についてはIntelliSenseの問題により解釈できないようです。原因はx64では__cdeclと__stdcallの呼び出し規約が同一視されるにもかかわらず、IntelliSenseは異なる関数として扱うためテンプレート展開に失敗するためです。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-20874895654621697392014-12-24T22:17:00.000+09:002014-12-24T22:18:12.330+09:00F#における非同期Socket<style>.gist div {margin-bottom: 0; line-height: 1.4;}</style>
.NET FrameworkにはVersion 1.0からSocketクラスがあり、APM; Asynchronous Programming Modelと呼ばれるBegin / End系メソッドが用意されています。しかし実際にはIOの度にIAsyncResultオブジェクトを作成する必要があり、ハイパフォーマンスなアプリケーションは実現しづらいものでした。
そのため、Version 2.0 SP1にてSocketAsyncEventArgsクラス及びAsync系のメソッドが新規に追加されました。こちらは内部状態を持つSocketAsyncEventArgsクラスを再利用することで効率の良い非同期処理が行えるものとなっています。なお、Version 4.5で導入された非同期処理とメソッド名の命名規則が一致していますが全くの別物となっています。<br/>
これをF#で扱えないものかと検索したところ<a href="http://www.fssnip.net/8L">F#-friendly SocketAsyncEventArgs</a>を見つけました。ただし、残念なことにSocketAsyncEventArgsクラスの設計思想を意識されておらず、毎回SocketAsyncEventArgsオブジェクトを再作成するだけの単なるwrapperでしかありませんでした。さらに言えばF#には非同期ワークフローもありますからこちらも利用したいところです。
仕方がないので自作してみました。
<script src="https://gist.github.com/sayurin/d65c62252db4ac5a10df.js"></script>
書いただけでまだ使っていないので動くかわかりません。<br/>
蛇足ですが、Socketクラスは内部でWinsockを使っていますが、このWinsockの機能の一つにaccept()で接続を受け付けると同時にrecv()を行うことができます。また対称にconnect()と同時にsend()もできます。こうすることでHTTPなど一般的なプロトコルでリクエストの送受信ができ、システムコール回数を減らし、システムの応答性能が向上します。Socketクラスはこの機能に対応しているため、今回の拡張メソッドにも含めています。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-27589863857623297862014-09-10T23:42:00.001+09:002014-09-10T23:53:55.961+09:00WPF ListView (GridView) のソート<style>.gist div {margin-bottom: 0;}</style>
WPFにはListViewのGridViewモードとDataGridの2つのコントロールで表形式の表示が行えます。DataGridの方はソート機能が組み込まれていますが、ListViewの方は自前でソートコードを記述する必要があります。MSDNにも<a href="http://msdn.microsoft.com/ja-jp/library/ms745786(v=vs.110).aspx">方法 : ヘッダーがクリックされたときに GridView 列を並べ替える</a>という記事が用意されていたりしますがいまいちパッとしません。
そこで簡単に扱えるようにライブラリ化しました。
使い方は<blockquote class="code"><ListView ItemsSource="{Binding SelectedValue.Files, ElementName=tree}"
<span style="color: red;">xmlns:v="clr-namespace:Sayuri.Windows;assembly=GridViewSortLibrary"</span>
<span style="color: red;">v:GridViewSort.IsEnabled="True"</span>>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="名前" <span style="color: red;">DisplayMemberBinding="{Binding Name}"</span> />
<GridViewColumn Header="サイズ" <span style="color: red;">v:GridViewSort.MemberPath="Length"</span>>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Right" Text="{Binding Length, StringFormat=N0}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</blockquote>こんな感じです。要点は<ul><li>xmlnsでアセンブリ・名前空間を指定します</li><li><ListView>に添付プロパティGridViewSort.IsEnabled="True"を指定します</li><li><GridViewColumn>にDisplayMemberBindingの指定があればそのプロパティでソートが行われます</li><li><GridViewColumn>にDisplayMemberBindingを指定できない場合はGridViewSort.MemberPathにプロパティ名をしてします</li></ul>
ソースコードはGistに貼り付けておきました。<script src="https://gist.github.com/sayurin/b9e73098a9818f38a358.js?file=GridViewSort.fs"></script>
<hr />作成にあたって次の2つの記事を参考にしました。<ul><li><a href="http://www.thomaslevesque.com/2009/03/27/wpf-automatically-sort-a-gridview-when-a-column-header-is-clicked/">[WPF] Automatically sort a GridView when a column header is clicked</a></li><li><a href="http://www.wpf-tutorial.com/listview-control/listview-how-to-column-sorting/">How-to: ListView with column sorting</a></li></ul>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-34492876473043158452014-08-16T11:40:00.002+09:002014-08-16T11:40:50.585+09:00F# のアセンブリ表現<style>table.code{margin: 10px;border: solid 1px black;}</style>
F#は言語としてはとても素晴らしい設計をしていますが、コンパイルされたアセンブリは結構残念だったりします。<a href="http://devadjust.exblog.jp/21010104/">F# + Entity Framewrok で ASP.NET WebAPI サーバ立てたら、返ってくる JSON がおかしい</a>という記事を見かけたのでスコープについてまとめておこうと思います。
まずF#言語で<a href="http://msdn.microsoft.com/ja-jp/library/dd233188.aspx">アクセス制御</a>するためにはpublic internal privateの3つのキーワードが用意されています。次にアセンブリでのアクセス制御についてはCorHdr.hに定義されています。関連部分を引用すると
<blockquote class="code">// TypeDef/ExportedType attr bits, used by DefineTypeDef.
typedef enum CorTypeAttr
{
// Use this mask to retrieve the type visibility information.
tdVisibilityMask = 0x00000007,
tdNotPublic = 0x00000000, // Class is not public scope.
tdPublic = 0x00000001, // Class is public scope.
tdNestedPublic = 0x00000002, // Class is nested with public visibility.
tdNestedPrivate = 0x00000003, // Class is nested with private visibility.
tdNestedFamily = 0x00000004, // Class is nested with family visibility.
tdNestedAssembly = 0x00000005, // Class is nested with assembly visibility.
tdNestedFamANDAssem = 0x00000006, // Class is nested with family and assembly visibility.
tdNestedFamORAssem = 0x00000007, // Class is nested with family or assembly visibility.
...
} CorTypeAttr;
// MethodDef attr bits, Used by DefineMethod.
typedef enum CorMethodAttr
{
// member access mask - Use this mask to retrieve accessibility information.
mdMemberAccessMask = 0x0007,
mdPrivateScope = 0x0000, // Member not referenceable.
mdPrivate = 0x0001, // Accessible only by the parent type.
mdFamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly.
mdAssem = 0x0003, // Accessibly by anyone in the Assembly.
mdFamily = 0x0004, // Accessible only by type and sub-types.
mdFamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly.
mdPublic = 0x0006, // Accessibly by anyone who has visibility to this scope.
// end member access mask
...
} CorMethodAttr;</blockquote>という感じです。
先に<a href="http://msdn.microsoft.com/ja-jp/library/ms173121.aspx">C#言語のアクセス制御</a>の説明をしておくとわかりやすいでしょうか。C#言語ではpublic private protected internal protected internalの5種類です。これがアセンブリとどのような対応をしているかというと非常にわかりやすく
<table class="code"><tr><th>C#クラス</th><th>アセンブリ表現</th></tr><tr><td>public</td><td>tdPublic</td></tr><tr><td>internal</td><td>tdNotPublic</td></tr></table><table class="code"><tr><th>C#メンバー</th><th>アセンブリ表現</th></tr><tr><td>public</td><td>mdPublic</td></tr><tr><td>private</td><td>mdPrivate</td></tr><tr><td>protected</td><td>mdFamily</td></tr><tr><td>internal</td><td>mdAssem</td></tr><tr><td>protected internal</td><td>mdFamORAssem</td></tr></table>となります。
さて本題のF#言語ですが、C#より種類が少ないはずなのに予想を超える複雑さをしています。まずクラスですが、<table class="code"><tr><th>F#クラス</th><th>アセンブリ表現</th><th>C#相当クラス</th></tr><tr><td>public</td><td>tdPublic</td><td>public</td></tr><tr><td>private</td><td>tdNotPublic</td><td>internal</td></tr><tr><td>internal</td><td>tdNotPublic</td><td>internal</td></tr></table>と順当です。次にメンバーですが<table class="code"><tr><th>F#メンバー</th><th>アセンブリ表現</th><th>C#相当メンバー</th></tr><tr><td>public</td><td>mdPublic</td><td>public</td></tr><tr><td>private</td><td>mdAssem</td><td>internal</td></tr><tr><td>internal</td><td>mdAssem</td><td>internal</td></tr></table>…はい、F#上ではクラス外からアクセスできなくなるprivateですがC#のinternalに相当しアクセス可能となります。まだ簡単に見えますか? 恐ろしいのはここからです。F#にもC#にも自動実装プロパティがあります。プロパティの値を保持するためにコンパイラが自動的にフィールド(backing field)を用意しプロパティアクセッサを実装する機能です。当然、コンパイラによる自動的なフィールドである以上、プログラムからアクセスできるべきではありません。実際、C#ではmdPrivate、privateフィールドと同等です。さてF#はそうではありません。<table class="code"><tr><th>F#メンバー</th><th>アセンブリ表現</th><th>C#相当メンバー</th></tr><tr><td>public</td><td>mdPublic</td><td>public</td></tr><tr><td>private</td><td>mdAssem</td><td>internal</td></tr><tr><td>internal</td><td>mdAssem</td><td>internal</td></tr><tr><td>backing field</td><td>mdAssem</td><td>internal</td></tr></table>…はい、privateメンバーと同様にC#のinternal fieldに相当します。まだ簡単に見えますか? 実はこの表はまだ不完全です。F#ではクラス自身のスコープがメンバーのスコープに影響を与えます。<table class="code"><tr><th>F#クラス</th><th>F#メンバー</th><th>アセンブリ表現</th><th>C#相当メンバー</th></tr><tr><td rowspan="4">public</td><td>public</td><td>mdPublic</td><td>public</td></tr><tr><td>private</td><td>mdAssem</td><td>internal</td></tr><tr><td>internal</td><td>mdAssem</td><td>internal</td></tr><tr><td>backing field</td><td>mdAssem</td><td>internal</td></tr><tr><td rowspan="4">private</td><td>public</td><td>mdAssem</td><td>internal</td></tr><tr><td>private</td><td>mdAssem</td><td>internal</td></tr><tr><td>internal</td><td>mdAssem</td><td>internal</td></tr><tr><td>backing field</td><td>mdAssem</td><td>internal</td></tr><tr><td rowspan="4">internal</td><td>public</td><td>mdAssem</td><td>internal</td></tr><tr><td>private</td><td>mdAssem</td><td>internal</td></tr><tr><td>internal</td><td>mdAssem</td><td>internal</td></tr><tr><td>backing field</td><td>mdAssem</td><td>internal</td></tr></table>…要するにpublicクラスのpublicメンバーだけがC#のpublic相当であり、それ以外はなんであれ全てC#のinternal相当です。ちなみにmutableでないフィールドにfdInitOnly(C#におけるreadonly)が付けられていないため、アセンブリ内からであれば書き換え可能という問題もあります。
これを踏まえて要望をまとめておきます。<ul><li>backing fieldはmdPrivateにして欲しい</li><li>privateメンバーもmdPrivateにして欲しい</li><li>internal / privateクラスであってもpublicメンバーはmdPublicにして欲しい</li></ul>といったところでしょうか。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-65826799240698458622014-06-09T22:50:00.001+09:002014-06-09T22:50:43.487+09:00艦これ 司令部室について<a href="http://connpass.com/series/369/">F# 談話室</a>の15回に参加し、艦これ 司令部室について発表してきました。これに合わせて<a href="https://github.com/sayurin/KanHQ">GitHub</a>でリポジトリも公開しました。<br />
COMの素晴らしさを力説し、また布教してきました。比較的に好感触でした。<br/>たいしたことをは書いていませんが、発表に使ったプレゼンテーションはこちら<br/>
<iframe src="https://onedrive.live.com/embed?cid=B4ADBA32BB97085A&resid=B4ADBA32BB97085A%2132188&authkey=AMSVjlZBVzKfBKU&em=2" width="402" height="327" frameborder="0" scrolling="no"></iframe>
<hr/>
<blockquote class="twitter-tweet" lang="en"><p>COMダメ、絶対! <a href="https://twitter.com/search?q=%23fsroom&src=hash">#fsroom</a></p>— 赤ペン先生テラモナギ (@teramonagi) <a href="https://twitter.com/teramonagi/statuses/475619020545073152">June 8, 2014</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-6591219742373202192014-03-12T23:58:00.000+09:002014-03-12T23:58:58.859+09:00WinFormsのTextBox ControlでのIME制御<style>.gist div {margin-bottom: 0;}</style>
WinFormsのTextBox Controlの話題です。とても基本的なControlですが落とし穴がありました。IMEで漢字変換中にフォーカスを失うと未確定文字はそのままIMEが持って行ってしまいます。
WinFormsがこのような挙動をすることを知らず何も制御していなかったために、自作のアプリケーションがクソ呼ばわりされる事態に陥ってしまいました。
とても悲しかったのでTextBoxを派生して挙動を改良してみました。
IMEが開いているかどうかは<a href="">ImeContext.IsOpen()</a>で調べることができますが、そのあと同じハンドルを使用して処理することになるため、IsOpenは使いませんでした。
<script src="https://gist.github.com/sayurin/9340629.js"></script>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-49159251906350388232013-12-14T01:31:00.000+09:002013-12-14T01:53:15.916+09:00続続 C は高級アセンブラ<style>.gist div {margin-bottom: 0;}</style>
<a href="http://sayurin.blogspot.jp/2013/12/c.html">C# は高級アセンブラ</a>、<a href="http://sayurin.blogspot.jp/2013/12/c_9.html">続 C# は高級アセンブラ</a>の続きです。
<br />
C#ではおよそ最適化できることろがなくなってきていて、最後のブレイクスルーとして、altriveさんの<br />
<blockquote class="twitter-tweet" lang="en">
<a href="https://twitter.com/haxe">@haxe</a> さんのコード見て、これ以上最適化できそうもないので、自分のコードも公開。<a href="https://t.co/rnekjQyrjx">https://t.co/rnekjQyrjx</a> 工夫してるのは下記の2点。
・標準入力が遅かったので、PInvokeでCのライブラリを呼び出し
・RadixSortを条件に合わせて最適化<br />
— altrive (@altrive) <a href="https://twitter.com/altrive/statuses/408956071844118528">December 6, 2013</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
を真似させてもらいました。標準入出力を扱うConsoleクラスはstaticメソッドになっているため、マルチスレッドセーフにするためにどうしても処理が重くなっていることはわかっていました。<br />
代替手段としてMono.Posixアセンブリに含まれているMono.Unix.Native名前空間、<a href="https://github.com/mono/mono/blob/master/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs#L3582" target="_blank">Syscall.read()</a>を使う案もありましたが、Assembly.Load()で読み込むことができずに没になりました。<br />
仕方がないため、libcのread()を直接DllImportしています。<br />
これによりめでたく<a href="http://paiza.jp/poh/ec-campaign/result/903d561d6eb6a5f09a06290691081ef2" target="_blank">Case3でも0.01のスコア</a>を出すことができました。逆に言うと、Consoleクラスのオーバーヘッドが0.02秒存在していることになります。ソースコードはこちら。
<script src="https://gist.github.com/sayurin/7821234.js"></script>
<br />
<hr />
ところでこの記事のタイトル「高級アセンブラ」ですが、どうも「私が『高級アセンブラ』とは『単にプリミティブっぽい処理を並べること』と解釈している」と誤解されている方がいるようなので説明しておきます。<br />
私なりの解釈では高級アセンブラとは、その言語のコードから生成されるアセンブリが容易に想像でき、また生成されるアセンブリ内容を制御すべくコーディングすることにあると考えています。<br />
例えば初回に公開したコードにはコメントに「引数4、変数4なので効率よく参照可能。」とあるようにレジスタ割り付けを考慮したコーディングを行っています。(C言語であればコンパイラにレジスタ割り付けを指示するregisterキーワードが存在しました。)<br />
と言うだけでは何なので、C言語でも書いてみました。スコアは普通に<a href="http://paiza.jp/poh/ec-campaign/result/c2fb77b572ea2d4ebf082f09d1bf2fcf" target="_blank">0.01 / 0.01 / 0.01</a>です。<script src="https://gist.github.com/sayurin/7924021.js"></script>
なお、しょうもないところでは #include IO_H の行。インクルードするファイル名は直接の文字列でなくても文字列定数を使うこともできます。もちろんコンパイラの出力を貼り付けただけだろうと言わせないためにも、今どきのコンパイラが使用しない AAh なんかを使ってます…実際には効率悪いとは思いますが。<br />
その他、C#とロジックは同じなので、÷10、÷100、÷1000、÷10000、÷100000が存在していますが、この辺りもコンパイラは逆数の乗算に変換します。…というだけなのも悔しいのでコードでは使用していませんが手計算で調べたメモを書いておきます。
<br />
<ul>
<li>0~99の範囲で x / 10 に相当するのは x * 205 >> 11</li>
<li>0~999の範囲で x / 100 に相当するのは x * 41 >> 12</li>
<li>0~9999の範囲で x / 1000 に相当するのは x * 8839 >> 23</li>
<li>0~99999の範囲で x / 10000 に相当するのは (int)(x * 429497L) >> 36)</li>
<li>0~999999の範囲で x / 100000 に相当するのは (int)(x * 687195L) >> 40)</li>
</ul>
<div>
コンパイラが生成する乗算の場合、被除数が特定できないため32bit精度を保証する逆数になりますが、被除数の範囲を絞り込むと必要となる有効精度が下がるため、乗数が小さくて済みます。<br />
乗数を小さくする利点は、乗算が高速に完了することもありますが、更にもう1つ、LEA命令が視野に入ってきます。LEAはアドレス計算する命令ですがこれを応用すると乗算を更に高速化できます。<br />
例えば、x * 41の場合、</div>
<blockquote class="code">LEA EBX, [EAX * 4 + EAX] ; EBX = EAX * 5
LEA EAX, [EBX * 8 + EAX] ; EAX = EBX * 8 + EAX = <b>EAX * 41</b></blockquote>
と2命令で表現できます。そしてx * 205も3命令です。
<blockquote class="code">LEA EBX, [EAX * 4 + EAX] ; EBX = EAX * 5
LEA EAX, [EBX * 8 + EAX] ; EAX = EBX * 8 + EAX = EAX * 41
LEA EAX, [EAX * 4 + EAX] ; EAX = EAX * 205</blockquote>
やりだすと本当に奥が深くなります。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-64608614269679528362013-12-14T00:07:00.000+09:002013-12-14T00:07:08.565+09:00F# と OCaml この記事は<a href="http://connpass.com/event/3935/" target="_blank">F# Advent Calendar 2013</a>に参加しています。14日目を担当させてもらいます。<br />
<h3>
F#とは</h3>
F#については<a href="http://msdn.microsoft.com/ja-jp/library/dd233154.aspx" target="_blank">MSDN</a>では<br />
<blockquote>
F# は、従来のオブジェクト指向プログラミングと命令型 (手続き型) プログラミングに加えて、関数型プログラミングをサポートするプログラミング言語です。 Visual F# 製品は、F# アプリケーションの開発と F# コードを使用した他の .NET Framework アプリケーションの拡張をサポートします。 F# は、.NET Framework 言語のファースト クラスのメンバーであり、関数型言語の ML ファミリに著しく似ています。</blockquote>
…と説明されています。MLファミリと書かれていますが、中でもオブジェクト指向的要素が追加されたOCamlとはかなりの類似点していて、F#で提供されるコアライブラリの基本部分はOCamlのモジュールと一致しています。そこでOCamlとF#の違い、その理由について探ってみようと思います。<br />
<h3>
F#とOCamlの違い</h3>
何よりも大きいのはオブジェクトおよびガベージコレクタでしょうか。F#は.NET Framework上で動作するため、すべてのオブジェクトは.NETオブジェクトでありSystem.Objectの派生クラスです。このことは全てのオブジェクトはSystem.Typeを通じてRTTI; Run-Time Type Infomationが得られることを意味します。対してOCamlは独自のGCでありSystem.TypeのようなRTTIは提供されていません。元々コンパイル時に型チェックされているため、実行時に型情報は不要なわけです。そのため実行時にObj.magicを用いてデータを強引にキャストしてしまうこともできます。これはC++言語でいうreinterpret_castであり、F# / .NETでは不可能な行為です。<br />
構文に対する細かい優先順位もところどころ違います。もちろん構文の違いといえばF#には独自の軽量構文がありますし私自身便利に使っていますが、OCamlとの比較においては論外ということで、それ以外について。 F#では.NET Frameworkで提供されるnamespaceが扱え、「.」(ピリオド)で区切ります。更にプロパティやメソッドも「.」でつながれ、正しいメンバー参照である限りどこまででもつなぐことができます。例えばSystem.String.Empty.Count.ToString()とか。しかしOCamlではUIDとLIDしかありません。UIDとは大文字から始まる識別子、LIDとは小文字から始まる識別子です。先ほどのObj.magicはObjがUIDでmagicがLIDとなります。namespaceはなくクラスメソッド参照は「#」となるため「.」の優先順位が異なります。<br />
構文といえば独自に新しい演算子が作成できる点、これはF#とOCamlと共通で、通常の.NET Frameworkからするとかなり異質な行為です。逆にコンピュテーション式はF#独自の機能です。コンピュテーション式内は通常のF#構文のままですが、書かれた式は直接コンパイルされるのではなく、構文解析後、コンピュテーション式のビルダークラスのメソッド呼び出しへと変換されてコンパイルされます。このような機能はOCamlにはありません。<br />
…というのは嘘で、Camlp4という特殊なモジュールがあります。Camlp4とはPre-Processor-Pretty-Printer for OCamlという意味です。一般的にコンパイラというのは、構文パーサーがソースコードからAST; Abstract Syntax Treeを構築し、ASTを何等かのバイナリに変換を行います。OCamlでは通常の構文パーサー以外にCamlp4が提供する別のParserに差し替えることが可能です。といってもただ構文パーサーを差し替えても同じ構文しか使えないのでは実装が2種類あるだけ何も面白くありません。独自のParserを作成しそれに差し替えることもできます。 ただし、独自のParserを全て作り上げるのは大変なことなので、新たに構文を増やさないのであればFilterというASTを別のASTに変換する機能もあります。これはF#のコンピュテーション式に近い行為ですが、どのように変換するかをプログラム的に制御できるため自由度は高いです。 また、ParserとFilterまで用意するならついでということでPrinterも用意されています。こちらはASTをコンパイルするのではなく別のデータ形式に変換することができます。それ以外にもいろいろありますが、あまり詳しくないのでこの辺りまでで。<br />
実はCamlp4はRevisedというOCamlとは別の構文で書かれています。つまりCamlp4をコンパイルするにはCamlp4のRevised Parserが必要であり、ある種のセルフホスト状態になっています。なぜそうなっているかというとOCamlの構文が気に食わないそうで、funとfunctionは同じものだからキーワードを分ける意味がないとか、変数と関数を同じletにすべきではないとか、どこかに書かれていました。<br />
<h3>
FSharp Printer for Camlp4</h3>
というわけで、Camlp4に含まれている<a href="https://github.com/ocaml/ocaml/blob/4.01/camlp4/Camlp4/Printers/OCaml.ml" target="_blank">OCaml Printer</a>を元に<a href="https://github.com/sayurin/FSharpPrinter" target="_blank">FSharp Printer</a>を作ってみました。Camlp4がparse可能なソースコードをF#コードに変換して出力できることになります。patch形式にしているのは、修正個所がわかりやすくなるのとASTの変更に追従を考えて、ですが…無意味かな?<br />
先に使い方を説明しておくと、コンパイルするにはコンパイラとパッチ元ファイルのバージョンを一致させる必要があります。現時点でリポジトリに含めているのは4.01.0向けのソースになります。コンパイル方法は、通常のOCamlと少し異なり、Camlp4を使います。
<br />
<blockquote class="code">
$ ocamlc -I +camlp4 -pp camlp4rf -c Camlp4FSharpPrinter.ml</blockquote>
でCamlp4FSharpPrinter.cmoが生成されます。<br />
使うためにはこのファイルを参照可能なディレクトリならどこでも構わないですがとりあえず、
<br />
<blockquote class="code">
$ cp Camlp4FSharpPrinter.cmo $ocaml/lib/camlp4/Camlp4Printers/</blockquote>
とコピーします(尚、パッチ元のOCaml.mlのあるディレクトリとは別です)。その上で、OCamlソースコードをF#コードに変換するには
<br />
<blockquote class="code">
$ camlp4of -printer Camlp4FSharpPrinter -o fsharp-source.ml ocaml-source.ml</blockquote>
と実行します。<br />
ということでコードをペタペタ。<br />
<script src="http://gist-it.appspot.com/github/sayurin/FSharpPrinter/blob/master/Camlp4Printers/Camlp4FSharpPrinter.patch"></script>
<br />
<h3>
F#とOCamlの違い</h3>
またですが…FSharp Printerを作っていて気づいた点をいくつか。<br />
F#の軽量構文はインデントで表現するため、メンバーの無いクラスを表現することができません。<a href="http://msdn.microsoft.com/ja-jp/library/system.runtime.remoting.messaging.ilogicalthreadaffinative(v=vs.110).aspx" target="_blank">ごくまれ</a>に存在するmarker interfaceのようなことが表現できなくて困ったりします。こういう場合でも冗長構文なら表現することができます。<br />
OCamlはモジュールをまたぐ構造体メンバーアクセスの際にはモジュール名を挟みます。someObject.ModuleName.memberNameという感じにいきなりモジュール名が出現するため心臓に悪いです(最初の方に書いたUID / LIDはこのことです)。F#では型情報は全て把握できているためsomeObject.memberNameになります。<br />
F#では配列を含むindexerが全て .[] 演算子で表現されます。そのため .[] が出現した時点でindexerの存在する型に確定していないとコンパイルできません。対してOCamlにはindexerという汎用的な概念はなく、配列要素にアクセスする .() と文字列にアクセスする .[] のみとなっています。つまり、F#とは逆でこれらの演算子から型推論することができます。この型推論条件の違いを埋め合わせるために、FSharp PrinterではArray.get / String.getに置き換えています。<br />
OCamlの==演算子、!=演算子も困ったことに。単純に=演算子、<>演算子に置き換えることはできません。とりあえずわかる範囲でパターンマッチに展開することにしました。この辺りの細かいOCamlの動作(及びそれに相当するF#のコード)はよくわかっていません。<br />
OCamlはパターンマッチに'0'..'9'のように文字範囲を表現することができますがF#にはありません。こちらはwhen句に展開しています。<br />
F#ではクラス内のletはprivateスコープですが、OCamlはいわゆるprotectedスコープであり派生クラスから参照可能です。こういった違いはさすがにFSharp Printerで埋め合わせることはできません。コンストラクターの書式も違いますがこちらは結構強引に対応させています。<br />
もっと大きな問題として、OCamlにあってF#に存在しないモジュールの呼び出し…これについてはF# PowerPackを使ってください。<br />
最後にどうにもならない爆弾を…。F#というか.NETには値型があるため効率のいい配列操作ができますが、OCamlはこれができません。そのため、stringがbyte arrayのように使われています。つまりOCamlのstringはmutableです。対してF#のstringはimmutableです。この違いはどうにも吸収できません。OCamlソースをF#に移植する際には、mutableに扱われているstringをbyte[]に変換するところから始まると言ってもいいでしょう。<br />
<br />
以上、とりとめもないF#とOCamlの比較でした! Camlp5…? 知らない子ですね。<br />
<br />
<ht>
ところでこのblogのテーマ読み辛い…。元々Pタグでレイアウトするつもりでスタイルを書いてたのに、エディターが更新されてDIVタグ&BRタグしか埋めてくれない…。</ht>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-2700538260306563442013-12-09T08:03:00.001+09:002013-12-14T01:32:58.431+09:00続 C# は高級アセンブラ<style>.gist div {margin-bottom: 0;}</style>
<a href="http://sayurin.blogspot.jp/2013/12/c.html">C# は高級アセンブラ</a>の続きです。<br />
何とかして高速化しようと思い試してみました。まずはボツ案から。<br />
<script src="https://gist.github.com/sayurin/7836635.js"></script>
あ、はい、ごめんなさい。
<br />
<hr />
時計屋さんからこのようなヒントを頂きました。
<br />
<blockquote class="twitter-tweet" data-conversation="none" lang="en">
<a href="https://twitter.com/haxe">@haxe</a> メモリが余裕ありそうなので、バケツぶっかけてどーなるか今から試してみよっかなって<br />
— 時計屋 (@NetSeed) <a href="https://twitter.com/NetSeed/statuses/409284855541682176">December 7, 2013</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" lang="en">
<a href="https://twitter.com/haxe">@haxe</a> ソート自体をO(n)で完了してるんですが、逆に定数が若干多めって言うかなんていうか。。。その辺が問題じゃないかなって<br />
— 時計屋 (@NetSeed) <a href="https://twitter.com/NetSeed/statuses/409308989671755776">December 7, 2013</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
これを元にソート方法を見直し…というよりソート行為がほぼ不要になったためについでにzero copyも取り入れたところ、逆にコードがキレイになってしまいました。
<script src="https://gist.github.com/sayurin/7821234/ec9e7c45b1e9e092341e41b5143d269f70c31d27.js"></script>
スコアも上々で、<a href="http://paiza.jp/poh/ec-campaign/result/14fb975116f80119e53b0d51b4163fe1">0.02 / 0.02 / 0.03</a>となりました。
<hr/>
更に続きを書きました。<a href="http://sayurin.blogspot.jp/2013/12/c_14.html">続続 C は高級アセンブラ</a>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-16590899144179180982013-12-06T19:02:00.000+09:002013-12-14T00:31:44.738+09:00C# は高級アセンブラタイトルに意味はありません。<br />
今、<a href="https://paiza.jp/poh/ec-campaign" target="_blank">新人女子プログラマの書いたコードを直すだけの簡単なお仕事です!|</a>というオンラインハッカソンがアツいです。特に野田さんのニーソは素晴らしいです。<br />
私はC#言語しか使えないので頑張って書いてみました。<a href="http://paiza.jp/poh/ec-campaign/result/a1237a2b2e3d7184f503d94f71fb2478" target="_blank">採点結果</a>は0.02秒 / 0.02秒 / 0.05秒の100点になりました。これが私の限界のようなのでgistに<a href="https://gist.github.com/sayurin/7821234/dceef8560eb20efddf087ed8cda71a2557cbf6a3" target="_blank">ソースコード</a>を公開しておこうかと思います。<br />
特筆することはない至極素直なコーディングに心がけましたが…ちょっとだけ解説を。<br />
<ul>
<li>Array.Sort()はネイティブコードで実行されるので速いはずですが、Monoの場合マネージコードで実行されます。</li>
<li>Array.BinarySearch()とMonoのArray.Sort()はComparison<t><t>やIComparer<t><t>で比較されますが、これは組み込みの比較演算子に比べて遅いです。</t></t></t></t></li>
<li>もっと言うとIEnumrable<t><t>は組み込み配列に比べて遅いです。</t></t></li>
<li>stringインスタンス生成にはUnicodeへの変換などが絡むため遅いです。</li>
<li>そもそもI/Oは遅いので呼び出し回数は可能な限り削減しましょう。</li>
<li>MSILには4引数、4変数までは個別にオペコードが用意されていますが、それ以上の引数・変数に対してはオペランドでインデックス指定するため1バイトずつ長くなります。結局JITでネイティブのレジスタアクセスに変換されるので、気のせいといえば気のせいですが。</li>
</ul>
<div>
こんなところでしょうか。</div>
<hr />
さらにブレイクスルーがあったので<a href="http://sayurin.blogspot.jp/2013/12/c_9.html">続き</a>を書きました。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-12096097669968076802013-08-25T09:55:00.000+09:002013-08-25T09:55:24.186+09:00F#の%a書式指定はとても扱いづらい<p>F#のPrintfモジュールはある程度の書式指定ができるようになっていますが、基になっているOCamlが貧弱なせいもあってString.Format()ほどの表現力はありません。
何か手はないかと考えたところ %a 書式指定を思い出しました。<a href="http://msdn.microsoft.com/ja-jp/library/ee370560.aspx">Core.Printf モジュール</a>には</p>
<blockquote>
2 つの引数を必要とする一般的な書式指定子。 1 つ目の引数は、2 つの引数を受け取る関数です。この関数の 1 つ目の引数は、指定した書式設定関数 (TextWriter など) に対応する型のコンテキスト パラメーターです。2 つ目の引数は出力する値であり、この値によって、該当するテキストを出力するか、返すかを指定します。<br />
2 つ目の引数は、出力する特定の値です。</blockquote>
<p>とあります。いまいちわかりづらいので英語版ドキュメントも確認しましたが同じ内容で意味がよくわかりません。念のためOCamlの<a href="http://caml.inria.fr/pub/docs/manual-ocaml/libref/Printf.html">module Printf</a>も確認します。</p>
<blockquote>
user-defined printer. Take two arguments and apply the first one to outchan (the current output channel) and to the second argument. The first argument must therefore have type out_channel -> 'b -> unit and the second 'b. The output produced by the function is inserted in the output of fprintf at the current point.</blockquote>
<p>なるほどわかりません。</p>
<p>とりあえずF#で書いてみたところ、非常に面倒くさいことが判明しました。1つ目の引数の説明「TextWriter など」が罠です。
なんとprintfとsprintfとで要求される関数が違いました。具体的に関数を挙げた方がわかりやすいので、現在時刻をコンソール出力してみます。</p>
<blockquote class="code">let formatter_for_printf (textWriter : TextWriter) (dateTime : DateTime) =
textWriter.Write("{0:MM/dd HH:mm}", dateTime)
let formatter_for_sprintf () (dateTime : DateTime) =
String.Format("{0:MM/dd HH:mm}", dateTime)
printfn "%a" formatter_for_printf DateTime.Now
sprintf "%a" formatter_for_sprintf DateTime.Now |> Console.WriteLine</blockquote>
<p>つまり同じ %a でも</p>
<ul>
<li>printfで出力する時は(TextWriter -> 'T -> unit) 'Tが必要</li>
<li>sprintfで出力する時は(unit -> 'T -> string) 'Tが必要</li>
</ul>
<p>ということのようです。</p>
<p>OCaml側は確認していませんがドキュメントからはそう読み取れないため、F#の(限りなくバグに近い)仕様かなと思います。</p>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-40311806073380119192013-05-29T22:50:00.000+09:002013-05-29T22:50:28.110+09:00DataContractJsonSerializerでDictionaryを表現 再び以前、<a href="/2008/12/datacontractjsonserializerdictionary.html">DataContractJsonSerializerでDictionaryを表現</a>という記事を書きましたが、あれから4年半経ち時代が変わりました。
.NET Framework 4.5から<a href="http://msdn.microsoft.com/ja-jp/library/hh194418.aspx">DataContractJsonSerializer constructor</a>は<a href="http://msdn.microsoft.com/ja-jp/library/system.runtime.serialization.json.datacontractjsonserializersettings.aspx">DataContractJsonSerializerSettings class</a>を引数に取り、このclassは<a href="http://msdn.microsoft.com/ja-jp/library/system.runtime.serialization.json.datacontractjsonserializersettings.usesimpledictionaryformat.aspx">UseSimpleDictionaryFormat property</a>を持っています。
そう、つまり…標準機能でDictionaryが表現できるようになっていました。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-35809722858947816902013-02-16T15:41:00.000+09:002013-02-16T15:42:58.652+09:00F#の||>演算子についてF#言語仕様には|>演算子と||>演算子と|||>演算子が定義されています。|>演算子は<a href="http://msdn.microsoft.com/ja-jp/library/dd233228.aspx">シンボルと演算子のリファレンス</a>にも掲載されていますし、比較的よく使われています。
<blockquote class="code">x |> f</blockquote>
とすると f(x)が実行されるものです。さて残りの2つ、これらは言語仕様には存在だけが記されており、それぞれop_PipeRight2、opPipeRight3にコンパイルされることしかわかりません。調べたところ、これらは
<blockquote class="code">let inline (|>) x f = f x
let inline (||>) (x,y) f = f x y
let inline (|||>) (x,y,z) f = f x y z</blockquote>
のようなものだということがわかりました。つまり左辺にはタプルを受け、右辺の関数にタプルの値を展開して引数として適用するものです。<br/>
<br/>
結論。高度に発達した関数型言語はググラビリティが非常に高い。(op_PipeRight2でググったけどこの話題が全然見つからなかった…。)佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-18899400207910707302012-12-10T22:43:00.002+09:002012-12-10T22:43:26.153+09:00-ms-filterは無意味<p>Microsoft Internet Explorerにはバージョン4.0で導入された<a href="http://msdn.microsoft.com/en-us/library/ms532847(v=vs.85).aspx" target="_blank">filterプロパティ</a>があります。IEバージョン8からCSS標準に準拠するために-ms-filterプロパティが導入されました。しかし導入経緯を理解せず、ただ単に記述しているサイトおよび解説サイトを散見するため、この記事を書きました。</p>
<a name='more'></a><p>まずfilterプロパティの書式は次のようになっています。</p>
<blockquote class="code">filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE');</blockquote>
<p>filterプロパティを書かれる方は当然ながら書式を理解しているとは思いますが、改めてよく見るとその特殊性に気付くでしょうか? progid: とコロンがあります。CSSではfont-size: 10px; のようにプロパティと値を挟む記号です。ピリオドもありますが、小数点ではありません。突然丸かっこも出現します。かっこの中や外にカンマもあります。シングルクォートで括られた文字列もあります。<br/>
このようにfilterプロパティはIE以外のCSS Parserからするととても特殊な書式で一つ間違えればCSS全体を構文エラーに陥れかねない存在です。</p>
<p>そこでMicrosoft社は-ms-filter導入時に書式を変更しました。</p>
<blockquote class="code">-ms-filter: "progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE')";</blockquote>
<p>-ms-filterプロパティの値は''か""で括られた単一の文字列です。これであればIE以外のCSS Parserも混乱することはありません。あくまでこの仕様は-ms-filterに適用され、filterは従来通りです。</p>
<p>よくある間違いその1はこの仕様変更です。-ms-filterの値が単一文字列となっていることを知らずに、filterと同じ内容を繰り返しているサイトを見かけます。</p>
<blockquote class="badcode">filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE');
-ms-filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE');</blockquote>
<p>これでは-ms-filterプロパティは構文エラーになりIE8以降は無視します。それでもうまく表示されるのはIE8以降もfilterプロパティを解釈しているからです。</p>
<p>よくある間違いその2はfilterと-ms-filterを並べて書くことです。</p>
<blockquote class="badcode">filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE');
-ms-filter: "progid:DXImageTransform.Microsoft.MotionBlur(strength=50),
progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1),
progid:DXImageTransform.Microsoft.Slide(Duration=2.500, slidestyle='HIDE')";</blockquote>
<p>IE4以降はfilterプロパティのみを、IE8以降はfilterと-ms-filterの両方ともを認識します。では-ms-filterを書く意義は…ありませんね。</p>
<p>正しくはIE7以前にも対応したい場合はfilterだけを書き他のブラウザーは混乱しないことを祈ります。そしてIE8以降だけに対応したい場合は-ms-filterだけを書くべきだと私は考えています。</p>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-75335842541496075202012-06-04T22:00:00.000+09:002012-06-04T22:00:24.622+09:00CSS Media Queries Hacksスタイルシートは認識するセレクター・プロパティにだけ適用されればそれで済むはずと考えていたこともありました。しかし、CSS animationのできるブラウザーではCSS Sprite画像を読み込んだ上でアニメーション、そうでないブラウザーではアニメーションGIF画像を読み込む、なんてことをする場合、どちらのブラウザーでもbackground-imageプロパティは認識されてしまうため、ブラウザーに応じて動作を変えることができません。<br />
<br />
そこで目を付けたのが<a href="http://www.w3.org/TR/css3-mediaqueries/">CSS Media Queries</a>という機能です。この機能を使うとブラウザーのサイズなどに応じてスタイルシートを変更することができます。<br />
各種ブラウザーには独自の拡張機能がありこれを利用するとブラウザーごとに異なるスタイルを適用することができます。昔あったスターハックの合法版といったところでしょうか。<br />
<ul><li><a href="https://developer.mozilla.org/en/CSS/Media_queries">Mozilla FireFox</a><li><a href="http://msdn.microsoft.com/en-us/library/windows/apps/hh453556.aspx">Microsoft Internet Explorer</a><li><a href="https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariCSSRef/Articles/OtherStandardCSS3Features.html#//apple_ref/css/rule/@media">Webkit (Apple SafariとGoogle Chrome)</a><li><a href="http://jp.opera.com/docs/specs/presto2.11/css/mediaqueries/">Opera(Version 12の時点では拡張機能なし)</a></ul>これを組み合わせるとこんな風に書けます。
<blockquote class="code">@media (-moz-touch-enabled:0), (-moz-touch-enabled:1){
/* Gecko 1.9.2 (Firefox 3.6)以降に適用 */
}
@media (-moz-device-pixel-ratio){
/* Gecko 2.0 (Firefox 4)以降に適用 */
}
@media (-ms-high-contrast: none){
/* Internet Explorer 10に適用 */
}
@media (-webkit-animation){
/* Webkit (Safari4以降 or Chrome)に適用 */
}</blockquote>簡単ですが動作サンプルを。
<iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/MkkKF/embedded/result,css,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-88136785337156868652012-03-03T22:47:00.000+09:002012-03-03T22:47:57.366+09:00IMG要素のonloadイベントについてふと見かけたので正しいのかわかりませんが、書き留めておきます。
<a href="http://keyframe.co.jp/blog/archives/418">IMG要素のonloadイベントがIE系でうまくいかないときの対処</a>とか<a href="http://akibahideki.com/blog/htmlcss/ie9onload.html">やっぱりIE9でもキャッシュ有効時のimg要素はonloadを無視する件</a>とか見つけました。
しかし、<a href="http://msdn.microsoft.com/en-us/library/cc197055(v=vs.85).aspx">onload event</a>を読むと
<blockquote>To ensure that an event handler receives the onload event for these objects, place the script object that defines the event handler before the object and use the onload attribute in the object to set the handler.</blockquote>
と
<blockquote>You need to set the image.onload before you set the image.src.</blockquote>
書かれています。
つまり、
<ul>
<li>IMGタグよりも前にイベント内容を含むSCRIPTタグを配置する</li>
<li>src属性を設定する前にonloadイベントを設定する</li>
</ul>
ですかねぇ。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-59737862014858885422011-07-13T20:23:00.000+09:002011-07-13T20:23:51.298+09:00IStructuralEquatableの使い方IStructuralEquatableについて今までいまいちわからずにいました。ググって出てきたページを読んでいたら使い方が微妙で違和感を覚えたためじっくり調べてみました。そうしたらもうちょっと使いやすいものだということがわかりました。<br />
<a href="http://msdn.microsoft.com/ja-jp/library/system.collections.structuralcomparisons.structuralequalitycomparer.aspx">StructuralComparisons.StructuralEqualityComparer</a>はオブジェクトそのものではなく、その要素を比較するIEqualityComparerを提供します。これを使ってIEqualityComparer<T>の実装例を書いてみました。
<blockquote class="code">public class StructuralEqualityComparer<T> : IEqualityComparer<T> where T: IStructuralEquatable {
public bool Equals(T x, T y) {
return StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
}
public int GetHashCode(T obj) {
return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
}</blockquote>
こう書いてしまえばどう使うのかわかりやすいでしょうか。このサンプルそのものは new StructuralEqualityComparer<int[]>() のように使えます。<br />
なんでこんなクラスをわざわざ作るのかというと、LINQで要求される比較演算子はどれもIEqualityComparerでなくIEqualityComparer<T>だからです。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-84396445321337185502011-02-01T20:02:00.000+09:002011-02-01T20:02:14.882+09:00TransactionScope classの使い方.NET Frameworkでtransactionを扱うには<a href="http://msdn.microsoft.com/ja-jp/library/system.transactions.transactionscope.aspx">TransactionScope class</a>を使いますが、嵌りやすいのでメモっておきます。<br>
<br>
class説明にはisolation levelについて記載されていませんが、デフォルトではSerializableになっています。例えばSQL ServerのデフォルトはReadCommittedなので勘違いしてしまいそうです。では、isolation levelを指定しようとすると、constructorにもpropertyにもそれらしいものがありません。実はpublic TransactionScope( TransactionScopeOption scopeOption, TransactionOptions transactionOptions )のTransactionOption classで指定することになります。
しかし今度は余計なparameterまで渡す必要があります。
<ul><li>TransactionScopeOption…これはRequiredがデフォルトです。</li>
<li>TransactionOptionsのIsolationLevel…これは指定したかったReadCommittedにします。</li>
<li>TransactionOptionsのTimeout…これは指定しないと初期化されず0になってしまい、timeoutなしになってしまいます。デフォルトの値にするためにはTransactionManager.DefaultTimeoutを指定します。</li></ul>
結局、
<blockquote class="code">using( var transaction = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.DefaultTimeout,
} ) ){
...;
transaction.Complete();
}</blockquote>とやることになるでしょう。佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0tag:blogger.com,1999:blog-7370796000776920436.post-167181045741901862010-12-29T10:57:00.002+09:002010-12-29T11:00:07.222+09:00IsDuplicated拡張メソッド気が向いたので何となく書いてみました。
<blockquote class="code">public static class Enumerable {
public bool IsDuplicated<TSource>( this IEnumerable<TSource> source ) {
var keys = new HashSet<TSource>();
return !source.All( keys.Add );
}
public bool IsDuplicated<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) {
var keys = new HashSet<TKey>();
return !source.Select( keySelector ).All( keys.Add );
}
}</blockquote>
IsDuplicated() つまり重複があるかどうかを判断する拡張メソッドです。<br />
巷にはDistinct()を使ったりして項目数を数えている方法もありますが、数えるということはIEnumerableを最後まで確認する必要があります。<br />
<br />
しかし、重複があるかどうか知りたいだけ、同じものが1ヶ所でもダブっていればそれが知りたい、そんなときに最後まで確認するのは無駄です。IsUnique()でなくIsDuplicated()というメソッド名にしたのもそのためです。どうせダブってるんでしょ? ダブってるなら早く教えてよ、と。<br />
<br />
仕組みは簡単。<a href="http://msdn.microsoft.com/ja-jp/library/bb359438.aspx">HashSet</a>を使います。<a href="http://msdn.microsoft.com/ja-jp/library/bb353005.aspx">HashSet.Add()メソッド</a>は戻り値として重複の有無を教えてくれるのでそれを使います。<br />
更に<a href="http://msdn.microsoft.com/ja-jp/library/bb548541.aspx">All()拡張メソッド</a>は1つでもfalseを見つけると即座に中断して返ってきます。<br />佐祐理http://www.blogger.com/profile/16992937986398474632noreply@blogger.com0