TypeProviderで生成したクラスをC#で使うための方法

型プロバイダー(Type Provider)のちょっとしたアレコレ – Bug Catharsis
http://zecl.hatenablog.com/entry/TypeProvderArekore

に勝手につけたし。ちょうど「消去型と生成型」のところを考えていたところで、消去型(IsErased=true)と、生成型(IsErased=true)では、何が「プログラミング的」に違うのか?という話です。

第一には型が消去してしまうので、別のアセンブリから参照するときにインテリセンスが効かない、プロパティなどの名前解決ができない、ってところで、

  • うちうちで使うならば「消去型」のままで ok
  • 別途公開する必要があれば「生成型」を使う

って感じですね。アセンブリをファイルとして残しておくことで、Visual Studio が参照できるようになる、っていう仕組みだと思います。なので、アセンブリ自体は、こんな形でテンポラリファイルとして残しておきます。

let outerType =
    ProvidedTypeDefinition (thisAssembly, namespaceName,
        typeName, Some(typeof), IsErased = false )
// 名前を付けてアセンブリを残す
let tempAssembly = ProvidedAssembly(System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), ".dll"))
tempAssembly.AddTypes <| [ outerType ]

このファイルは、テンポラリファイルなので、生成された後に適当な時間で消えてしまいます。あと、TypeProvider の DLL はコードを変えるたびに生成されるので、テンポラリ名にして名前が変化するようにします。

■C# からタイププロバイダを使うときには、F# を媒介させる

型生成はテンポラリファイルとして残っているものの、これを参照できるのは F# からだけです。何故、F# からのみしか参照できないのかはわかりませんが、C# から参照しようとしても見当たりません。
といいますか、型生成の方法が、こんな風な構文になっているので、構文的に C# では書けないというのもありますね。

type AA = CSharpTypeProvider.STR<"masuda">
let a = new AA()

printfn "CSharpTypeProvider test in F#"
printfn "%A" a.Name
printfn "%A" a.Version

ただし、タイプビルダー自体は、

TypeBuilder クラス (System.Reflection.Emit)
http://msdn.microsoft.com/ja-jp/library/system.reflection.emit.typebuilder(v=vs.110).aspx

によって実現されているので F# しかできないものではありません。そのうちに C# や VB でも使えるようになる可能性はあります(構文さえ作ればよいので)。

さて、ここで作成した AA クラス あるいは CSharpTypeProvider.STR<“masuda”> というクラスを C# でどうやって扱えばいいのか?という話があまり書いていないのですが、結構簡単です。型生成だけした F# のライブラリを用意して、次のようにつくっておきます。

namespace FSharpLib
type AA = CSharpTypeProvider.STR<"tomoaki">

そして、C# のプロジェクトから FSharpLib プロジェクトを参照設定して、次のように書けばいいのです。

class Program
{
    static void Main(string[] args)
    {
        // refer SampleType.tomoaki.dll
        var a = new FSharpLib.AA();
        Console.WriteLine("Typeprovider test in C#");
        Console.WriteLine("{0}", a.Name);
        Console.WriteLine("{0}", a.Version);

        System.Console.ReadKey();
    }
}

簡単ですよね。クラスの定義部分が F# になってしまうために、C# 内で閉じないのが難点ではありますが、T4 などを使わずに型生成が簡単にできるのは非常に便利です。この型生成に渡すパラメータを内部的に解釈を変えれば、外部ファイルを読み込んだり、インターネットからダウンロードして生成したりすることもできます。外部ファイルへの対応は、

ちょっと草植えときますね型言語Grass型プロバイダー
http://github.com/zecl/GrassTypeProvider

にあります。

注意したいところは、コード変えるたびに型生成の実行が発生するので、あまり重たい処理は入れられないということです。重たい処理の場合は、別プロセスにするとか遅延を許す感じでユーザーに通知するなどの工夫が必要でしょう。

■型生成済みのアセンブリを C# で直接参照する

動的に型生成をする場合には、上記のように F# プロジェクトが必要ですが、最初に生成しただけであとは全く同じ状態であるならば、C# プロジェクトから直接生成済みのアセンブリを参照することもできます。

let outerType =
    ProvidedTypeDefinition (thisAssembly, namespaceName,
        typeName, Some(typeof), IsErased = false )
// フルパスで指定する
// 型を残して C# から直接参照できるようにする
let tempAssembly = ProvidedAssembly("d:\temp\SampleType."+ str+".dll")
tempAssembly.AddTypes <| [ outerType ]

先に書いたテンポラリファイルへの書き込みを逆手にとって、同じファイルに出力するようにします。ここでは、渡された文字列と組み合わせてユニーク名にしたアセンブリを temp フォルダに保存しています(フルパスでないと駄目です)。タイププロバイダに渡した初期値ごとに違うのですが、同じ型であれば同じアセンブリ名になります。
型生成する F# のプロジェクトは別に作っておいて、一度だけ動かします。
そして、生成した型生成の DLL を C# プロジェクトから参照させます。

static void Main(string[] args)
{
    var a = new CSharpTypeProvider.AA();
    Console.WriteLine("Typeprovider test in C#");
    Console.WriteLine("{0}", a.Name);
    Console.WriteLine("{0}", a.Version);

    System.Console.ReadKey();
}

すると、普通の DLL のようにクラスを生成することができます。型生成されているアセンブリだから当然ですね。
この方式のメリットは、C# のプロジェクトから F# のプロジェクトを参照しなくてよいことです。生成済みの DLL だけを使えばいいので配布も簡単です。まあ、大量に型生成する必要があるかどうかは別なのですが、状況次第ではいいのではないでしょうか?

■サンプルコード

動作確認のためのサンプルコードはこちら

moonmile/SampleTypeProviderToUseCSharp
http://github.com/moonmile/SampleTypeProviderToUseCSharp

カテゴリー: C#, F# | TypeProviderで生成したクラスをC#で使うための方法 はコメントを受け付けていません

最新の FsYacc/lex 環境で chulang を作成するときのメモ

勢いで作り始めた chu-lang.com ですが、ちまちまと lex/yacc のパターンで書き直し。どうせならば、F# の FsLex/FsYacc で作ろうかと思ってやっているのですが、結構はまります。

F#のfslexとfsyaccを用いたコンパイラ作成 (2/4):CodeZine
http://codezine.jp/article/detail/5355?p=2
fslexとfsyaccを使って字句・構文解析して電卓っぽい計算をしてくれる奴を作成したい – My Life as a Mock Quant
http://d.hatena.ne.jp/teramonagi/20120506/1336290544
VS2013でFsYacc、FsLexを使う – komorebikoboshiのブログ
http://komorebikoboshi.hatenablog.com/entry/2014/05/23/225132

もともと私の手元には Powerpack-4.0.0.0 が入っていて、少し事情が違うのですが、だいたい環境は同じです。なんか、lex/yacc の実行ファイルから Visual Studio から自動でビルドできるところまで、追っていました。が、*.targets の設定がうまく動かないのか?(F# 3.0 の環境だと動くのか?)、妙に環境が整えづらいんですよね。FsLex のビルドも遅いし…と、再び探すと。

FsLex, FsYacc
http://fsprojects.github.io/FsLexYacc/

最新版は、Nuget から取って来る、という状態になっているようです。github から取って来れば、サンプルコードとサンプルプロジェクトがついてきて、最新の fslex.exe と fsyacc.exe が packages の中にダウンロードされます。便利ですね。

■Nuget から FsLexYacc を取ってくる

lex/yacc 用の DLL は、FsLexYacc.Runtime という名前に代わっています。

Lexer.fsl と Parser.fsy を使ってビルドするプロジェクトは、上のほうを選択します。実際に使うときは、Runtime だけのプロジェクトで Ok ですね。

■ビルドされるように *.fsproj を書き換える

http://github.com/fsprojects/FsLexYacc/blob/master/tests/TestProjectUsingNugetPackage/TestProjectUsingNugetPackage.fsproj#L86

を参考にして、Lexer.fsl と Parser.fsy を追加します。

  <ItemGroup>
    <None Include="Lexer.fsl" />
    <None Include="Parser.fsy" />
    <FsYacc Include="Parser.fsy">
      <OtherFlags>--module Parser</OtherFlags>
    </FsYacc>
    <FsLex Include="Lexer.fsl">
      <OtherFlags>--unicode</OtherFlags>
    </FsLex>
  </ItemGroup>

あと FsYacc と FsLex が有効になるように *.targets をインポートします。

  <Import Project="..\packages\FsLexYacc.6.0.3\bin\FsLexYacc.targets" />

FsLexYacc.targets の呼び出しにバージョンが付いてしまっているので、Nuget が最新になると呼び出せない…という不具合はあるのですが、まあ、こうしておきます。*.fsproj に FsLexYacc.targets をコピーして動かすと、Build.dll がない状態になるので、仕方がないですね。

以前は、ビルド時に

"C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\\bin\fslex.exe"  "$(ProjectDir)\Lexer.fsl" --unicode
"C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\\bin\fsyacc.exe" "$(ProjectDir)\Parser.fsy" --module Parser

なことをやって、毎回ビルドしていたのですが、これでファイルが更新されたときにビルドされるようになります。

■PCLで動くのか?

結論から言うと、FsLexYacc.Rutime は PCL では動きません。ちなみに Powerpack 版も動かなかったので同じ状態です。

ただし、これはソースが github で公開されているので、Profile78 あたりでビルドし直してやれば、Xamarin.iOS/Android で動くようになるのでは?と思っています。あと Windows Store アプリのほうも。

■chu-lang とは?

絵文字で書く関数型プログラム言語です。何処を目指しているかは謎です(いや、実ははっきりしてるんだけど、いまのところは「謎」)。たぶん、タブレットとかスマートフォンで打ちやすい言語になるはずです。

http://chu-lang.com/

こんな感じで動く予定。

カテゴリー: F#, chu | 1件のコメント

WindowsストアアプリでXAMLを動的にロードする方法

XamlReader.Load method (Windows)
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.markup.xamlreader.load.aspx

を使うと、XAML ファイルを動的にローディングできるんやで、ということは以前 XAML を調べていて分かったのだけどサンプルがいまいちだったので作ってみました。

ちなみに、Dev Center のサンプルは、

"<Ellipse Name="EllipseAdded" Width="300.5" Height="200" 
Fill="Red" "http://schemas.microsoft.com/winfx/2006/xaml/presentation"/>";

のところを、

"<Ellipse Name="EllipseAdded" Width="300.5" Height="200" 
Fill="Red" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"/>";

にして「xmlns=」を追加しないと動きません orz.

■XAML を文字列で指定する。

サンプルコードのように xaml 文字列を作成して、XamlReader.Load に渡すとルートのオブジェクトが帰ってきます。先の例では、Ellipse オブジェクトが取れるわけです。
これをどのようにすり替えるかというと、普通に Children.Add します。なので、ページを切り替えようと思って Frame.Navigate しようと思ってもうまくいきません。Navigate に渡すのはクラス自身なので、オブジェクトじゃないんですよね。仕方がないので、Page.Content の方をすり替えます。

private void OnClickString(object sender, RoutedEventArgs e)
{
    // var xaml = "<TextBlock FontSize="50" Text="From XAML" "http://schemas.microsoft.com/winfx/2006/xaml/presentation" />";
    string xaml = "<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><Ellipse Name="backbtn" Width="300.5" Height="200" Fill="Red" /></Grid>";
    var doc = XamlReader.Load(xaml) as Grid;
    this.Content = doc ;
    // 短いXAML文字列だと FindName できる?
    var el = doc.FindName("backbtn") as UIElement;
    el.Tapped += (_,__) =>
    {
        this.Content = originalGrid;
    };
}

こんな風に、文字列を Page(this)のContent プロパティに設定すると画面が切り替わります。
戻すためにボタンをつけておいて、x:Name でつけること…は実はできないはずなのですができてしまいます。たぶん、XAML の文字列が短い場合は大丈夫なのかもしれません。次の HTTP プロトコルでデータを取ってくるときはうまくいかないのですから(MSDN にも x:Name は「使わないで」と書いてあるので)。

味気ない楕円の○が出ます。これをクリックすると元の画面に戻ることができます。

■HTTP プロトコルで XAML データをダウンロードして表示する

XAML 文字列で表示できたということは、インターネット上にあるデータを取ってくることもできます。HttpClient#GetStringAsync でがっつりともってきて表示すれば Ok です。

private async void OnClickHttp(object sender, RoutedEventArgs e)
{
    var url = "http://moonmile.net/up/samplepage.xaml";
    var cl = new HttpClient();
    var xaml = await cl.GetStringAsync(new Uri(url));
    var doc = XamlReader.Load(xaml) as Grid;
    this.Content = doc;
    // 戻るボタン
    // HTTP からロードしたときは x:Name がスルーされない?
    // var el = doc.FindName("backbtn") as UIElement;
    var el = doc.Children.First<UIElement>( t => { 
        var btn = t as Button;
        return (btn != null && ((string)btn.Content)=="back" )? true : false;
    });
    el.Tapped += (_, __) =>
    {
        this.Content = originalGrid;
    };
}

文字列が長いためか、x:Name が有効に働きません。仕方がないので。Button#Content で戻るボタンを取ってきているのですが、これは別な手段を考えたほうがいいでしょう。

こんな風に、Grid と Button, 楕円を並べた XAML を表示させることができます。ただし、すべての XAML がロードできるわけではなく、かなり制限があるようです。

  • x:Name が設定できないので、目的のコントロールを探すのがしんどい。
  • Stroyboard がうまく設定できない(実行エラーになる)のでアニメーションができない。
  • トリガーの設定はできるのか不明

な感じです。アニメーション&トリガーの組み合わせができないのは痛いですよね。おそらく、Storyboard 自身への x:Name と targetProperty のあたりでエラーになっているようです。まあ、コードビハイド(というのか?)で、独自に storyboard を追加してやって適度にイベントを設定すればそれらしいことはできるのですが、HTMLとSVGの組み合わせでアニメーションという具合にはいかなそうです。
あと、XAML への C# コードの埋め込みはできるのか?という問題がありますね。埋め込みができれば、View のままで色々できるので、Model と View の完全な分離(ダイナミックにリンクさせるという意味で)はかなり実用的になりそうなのですが。

■サンプルコード

WinStoreDynamicView
http://github.com/moonmile/WinStoreDynamicView

カテゴリー: C#, XAML | WindowsストアアプリでXAMLを動的にロードする方法 はコメントを受け付けていません

WPF は死んだのか?

というか、Microsoft としては WPF をどうする気なのか?が気になるところです。by #comuplus ですね。表向きとしては、Windows DNA の頃(最初に XAML が発表された頃)と同じで「XAMLを学んでおくと、Windows Phoneでの開発がやりやすくなる」だと思うのですが、日本ではいまだにWindows Phoneが発売されない変な状況と、欧米諸外国の反応をかんがみれば、日本と日本以外のアプローチはかなり違っています…が、求めるところは同じところかな、とは思われるんですがね。

デスクトップでの WPF アプリケーションとしては、提督業も忙しい! を代表として、結構「綺麗な」アプリケーションができることがわかっています。もともと、ぐらばくさんがビデオ操作アプリ(でしたっけ?)の UI に特化していたのもあって「WFP で何が作れるのか?どこまでいけるのか?」の実験的な雰囲気もありました。あの、Visual Studio の光る影を作るところからスタートなハズです。で、Windows フォームでは作りづらかった(Visual Studio の 光る影は、がりがりに Widnows API なんですけど)綺麗な画面が、WPF で手軽に作れるところが最初の売りだったのです。従来の Windows Forms で作ると、テキストボックスの背景に色をつけるとか、コンボボックスのサイズを自由に変更して画像を入れ込むとか、が大変だったのです、DrawItem を使わないといけませんでしたから。コンポーネントを組んで発売したのが、例の旧文化オリエントで、COM の前身の VBX の頃からリッチなコンポーネントを販売していました(実は旧文化オリエントが作っていたのではなくて、イスラエル?だっけ?から買っているコンポーネントです。現在は、インドで作っていますね)。このあたりの、VBX → COM の流れを探ってみると、先の comuplus の話が分かりやすいのですが。そうそう、VBX 自体は、C++ でしか書けなくて、VB のコンポーネントを C++ で書くという言語差がありました。それを OCX とすることで、VB 自体でもコンポーネントを作れるようにしたんですよね。そのあたりが、従来の IUnknown とアパートメントの歴史になります。VB のほうは名前付けですからね。

さて「綺麗な画面」を形作るのは、実は WEB ブラウザで HTML 形式によるアプリケーションが出てきたのと無関係ではありません。当時、HTML で作ったブラウザアプリが画像を使って華やかになってくる、さらに Shockwave, Flash などを使ってアニメーション効果などが入った頃に、かたやデスクトップ環境の Windows フォームアプリは相変わらずの「灰色な画面」が標準的だったのです。一方で、DHTML を使ったアプリを IE 上で動かそうとしたり、あれこれと Web 業界に踏み込んだ Microsoft なのですが、やっぱり Apache に勝てなかった…んですよね。今はどうかわかりませんが、いや、Azure に LAMP 環境を乗せるという形で共存の道を選んでいます。

そのなかで、「統合」という形で、デスクトップの画面を HTML 形式風に扱えるようにしたのが、WPF です。かつ、Sliverlight ですね。この両方とも XAML という書式を扱っているのがミソで、デスクトップ環境でも動く、ブラウザ環境でも動く、ってのが最大のメリットでした。それ以前の UI の構築方式は、

  • VB6.0 のように独自の書式をつかう。
  • VC++ のようにリソースとして埋め込む。
  • tcl/tk のようにコードで構築する。

というスタイルが標準だったのです。そこで、HTMLかつXMLという標準的なフォーマットが生まれ、XLST などの派生ツールができたあたりで(これは死んでるのかな?)、標準フォーマットに XML を使おうといいうスタイルが生まれてきたわけですね。最初の、WPF1.0 や Silverlight が貧弱すぎてイベントまわりがひどかったので、SVG との差別化が難しかったのですが、バックグラウンドに .NET フレームワークと直結させたところに、WPF(XAML)のメリットがあります。

さて、当時「なぜ XAML を学ぶのか?」の質問に対して Microsoft は常に「統合されたときにあらゆるところで XAML が使えるようになるから、学んで損はない」という回答でした。これが、広告的なものだったのか、本音だったのかは定かではありませんが、今となって(非常に遅い登場ではありましたが)、Windows Store アプリと Windows Phone アプリでほぼ共通の XAML が使えるようになったことが、それを示しています。

その中で、MVVM パターンという View と Model の作成パターンの含めて(これは戦略的なものだったのか?少なくと WPF1.0 の頃にはなかったものですし、MVC を広めた後に、MVVM ですから、「幸運」かもしれません)、XAML の価値は MVVM とワンセットになって広がります。

独自形式であった Xcode の xib が storyboard になり XML 形式になって扱いやすくなったり、Android の UI が AXML 形式であったりする(これは単純に XML 形式です)したところから、「UI を XML 形式で分離させる良い」という風潮が広まり、同時に MvvmCross のような MVVM パターンを組み入れたライブラリが、Microsoft 以外にも広まるようになりました。

そうなると、XAML という Microsoft 独自形式ではなくても、XML で扱えば何かと UI が便利である、ことがわかってきます。静的に UI のプロパティは struts や ASP.NET で行われているので、それと同じように、静的にマッピングする方法を XAML が行い、それに storyboard や axml も追随しています。動的ローディングのほうはどうなのか?という話ですが、実は、XAML が発表される以前に、MyXaml で実現されています。と言いますか、もともと XAML は動的ロードになる予定だったのが、いつの頃なのか、静的ロードになったんですよね。ちなみに、Xamarin.Forms は静的ロードですが、(訂正)XAMLは動的ロードされています。Xamarin.Forms もリソースから動的ロードですよね。ただし、イベントのバインドとかも自動で行われる XamlReader を使って読み込まれます。ただ、これに癖があってイベントのバインドとか自動で行ってしまうのでバインド先がないとエラーになっちゃうのです。うまくやれば、HTTP プロトコルでネットからダウンロードして表示ってことも可能です。なので、 自前の Xamarin.Forms のプレビューアは別途動的ロードになっています。考えてみれば、ブラウザで表示される HTML のロードは動的ロードなので、静的に(ビルド時に)マッピングしなければいけない理由はないのです。当時、CPU パワーが遅かったので、初期表示時のロードを避けたのですが、いまとなっては結構なスピードで動きます。

そんな訳で、初期の頃には、WPF は XAML として孤立してた(XAML自体が孤立していた)のですが、今に至ると WPF は XAML の仲間となっています。Windows Store App や Windows Phone 8.1 アプリに XAML が使われると同様に、WPF でも XAML を使えます。それぞれのプラットフォームの違いにより使えるライブラリやコンポーネントの違いはありますが、基本的なところはほとんどかわりませんし、インターフェース自体(状況依存プロパティやイベントの作り)は変わらないので、UI ライブラリを移植するのはそれほど難しい作業ではありません(DirectX 絡みのところは大変でしょうが)。

というわけで、UI を XML 形式で作る、そして MVVM パターンを使ってプロパティとイベントを連結させる(ここの分離は Rx を使っても同じ、あるいは直接コードビハイドでも同じ)パターンとしては、

  • Windows Store App を XAML で書く。
  • Windows Phone 8.1 アプリを XAML で書く。
  • Xcode で iPhone/iPad アプリを Storyboard で書く。
  • Android で axml で書く。
  • Xamarin.iOS/Android で、storyboard, axml で書く。
  • Xamarin.Forms で、Xamarin製XAMLで書く。
  • WPF で XAML で書く。
  • Siverlight を XAML で書く。

という広がりまでできてます。そういう中で、Windows デスクトップアプリとして WPF を使った書くこと(書いておくこと)は、他のプラットフォームの移植しやすいという利点があります。惜しむらくは、このなかに Linux が含まれていないことと、ブラウザ環境が含まれていないことですよね。本来は Silverlight がそれを担っていたのですが、およそ死んでいます。XAML → HTML コンバータ、あるいは XAML を解釈するスタイルで、ブラウザのほうに XAML 形式を持っていければ(MVVM パターンも込みで)結構いいところまでいけるんじゃないんですかね?と期待はしているのですが → http://xaml.jp/

カテゴリー: 雑談 | WPF は死んだのか? はコメントを受け付けていません

はじめての chu ネタサイトを作りました

二週間ほど前に流れてきたツイートなんですが、chu 言語を作ればいいわけで、なるほど…と思っていたのです。どうせならば、.NET 環境で動くものがいいし、どうせならば F# っぽい関数言語がいいし、どうせならば chu に関したものがいいですね。ってことで、作ってみました(笑)。

http://hajimete-no-chu.com/ 
http://chu-lang.com/

image

swift で絵文字が使えるならば、いっそのことプログラム言語のキーワード自体に絵文字を使ってみようという試みです。<- の代わりに、 を使ったり、let の代わりに、 を使ったりします。いちいち、絵文字を打つのは大変なので、

    ://: F# like
    :let: :heart: :<-: "I"
    :let: :blue_heart: :<-: "chu"
    :fun: :smiley: :cat: :dog: :->: :cat: + ":heart:" + :dog:
    :let: :dolphin: :<-: :smiley: :heart: :blue_heart:
    :print: :dolphin:
    > I:heart:chu

な風に書くと、以下な風に書き換える javascript が仕込んであります。キーワードは

image

どうせならば、きちんと動くものを作りたいので、現在文法を練り込み中。HSP ぐらいには動くものを作っておきたい。

絵文字の画像と名前は、以下から借用しています。

Complete List of Emoji
http://www.fileformat.info/info/emoji/list.htm

カテゴリー: chu | はじめての chu ネタサイトを作りました はコメントを受け付けていません

通常のプログラムでも PrivateObject を使う方法

[C#]ユニットテストで private なメンバにアクセスしよう | 星空は撫子色
http://muu000.net/wordpress/?p=1691

実は、PrivateObject の存在を知らなかったんですよ。私の場合、テストしやすいように protected にしておくか、

Silverlight の UnitTest | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/330
PrivateObject クラス (Microsoft.VisualStudio.TestTools.UnitTesting)
http://msdn.microsoft.com/ja-jp/library/Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.aspx

な感じで、internal にして、InternalsVisibleTo でアクセス許可を与えています。古い TDD の議論で private メソッドにアクセスするのは云々、という話もありますが、複雑怪奇なアルゴリズムを作るときは細かくメソッド分割をしてテストをしたほうがよいので、最終的に見せるのはひとつのメソッドだけど内部的には十数個のメソッドの組み合わせにしているというパターンがよくあります。F# の場合は内部関数を使って入れ子にしたりする訳で。まあ、最初は public で書いておいてテストが済んだら private に変換するってのが一番お手軽かと。

■リフレクションの GetTypeInfo().GetDeclaredField() を使う

MSDN のマニュアルを見ると、GetDeclaredField メソッドは public なフィールドを取得するはずなのですが、試してみると private フィールドも拾えます。なんででしょうね?例の WinRT から COM アクセスするときに、アクセスできるようになっちゃったのかもしれませんが、このあたり良く調べていません。
が、以下な感じで、プライベートなフィールドが取得できます。

public object GetField(string name)
{
    Type t = this.RealType;
    var pi = t.GetRuntimeField(name);
    if (pi == null)
    {
        // MSDN ではパブリックフィールドになっているが、プライベートも取得できる
        pi = t.GetTypeInfo().GetDeclaredField(name);
    }
    if (pi != null)
    {
        var obj = pi.GetValue(this.Target);
        if (obj != null)
        {
            return obj;
        }
    }
    return null;
}
public T GetField<T>(string name)
{
    object o = GetField(name);
    return o == null ? default(T) : (T)o;
}

一旦、GetRuntimeField で拾っているのは、public なフィールドをチェックしている訳で、いきなり GetDeclaredField で調べても構いません。元ネタの Microsoft.VisualStudio.TestTools.UnitTesting のほうは、戻り値が object だけだったので、型変換できるようにしています。
このコードは PCL 用なので GetRuntimeFiled を使っていますが、Native ライブラリの場合もそのまま使えます。

テストコードは、こんな感じです。

適当な private だらけなクラスを作っておきます。

/// <summary>
/// for test sample class
/// </summary>
public class TestSample
{
    private int privateInt = 10;
    private string privateStr = "masuda";

    private int _privateProp = 20;
    private int privateProp
    {
        get { return _privateProp; }
    }

    private int privateMethod(int x, int y)
    {
        return x + y;
    }

    public void init( int a, string b )
    {
        privateInt = a;
        privateStr = b;
    }
    public int GetInt() { return privateInt; }
    public string GetStr() { return privateStr; }
}

それでもって、自作の PrivateObject でアクセスします。

public void TestGetField()
{
    var target = new TestSample();
    var o = new D4Reflection.Lib.PrivateObject(target);

    target.init(1, "tomoaki");
    Assert.AreEqual(1, o.GetField<int>("privateInt"));
    Assert.AreEqual("tomoaki", o.GetField<string>("privateStr"));
    target.init(10, "masuda");
    Assert.AreEqual(10, o.GetField<int>("privateInt"));
    Assert.AreEqual("masuda", o.GetField<string>("privateStr"));
}

■プライベートなフィールド/プロパティ/メソッドにアクセスする

moonmile/D4Reflection
http://github.com/moonmile/D4Reflection

この中にある D4PrivateObject プロジェクトがそれですね。

http://github.com/moonmile/D4Reflection/blob/master/D4PrivateObject/PrivateObject.cs

これだけをコピーしても大丈夫です。

■何に使うのか?

Windows Store app の場合、x:Name で作ったオブジェクトは private なんですよね。だから、この方式を使っています。同じアセンブリならば FindName を使ってもいいわけですが、Xamarin.iOS/Android と共通に使うとなると結構厄介だったので、最終的に private フィールドを直で持ってきます。

そうそう、名前で検索するのは面倒なので、TypeProvider を使えないかなと…思ったけど C# だとうまく動かないわけで、そうなると T4 の出番ですかね。

カテゴリー: C# | 通常のプログラムでも PrivateObject を使う方法 はコメントを受け付けていません

PCL の DLL からプロファイル名を表示するツールを公開

PCL (Portable Class Library) – Xamarin 3 の新しいコード共有テクニック : XLsoft エクセルソフト
http://www.xlsoft.com/jp/products/xamarin/portable_class_libraries.html 
Xamarinと、ポータブル・クラス・ライブラリ(PCL) – Build Insider
http://www.buildinsider.net/mobile/insidexamarin/13 
Xamarin.iOS/Android に対応している Profile78 とは何か? | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6137

ひとまず、ポータブルクラスライブラリで作っておけば、Xamarin.iOS/Android 間で共有できる訳ですが、Windows Store App や Windows Phone 8.1 を含めるとややこしいわけで、それなりに取捨選択しないといけません。

が、PCL 作成時に対象のフレームワークを選択せねばならず、将来的な未知のフレームワークにはどう対応するのか?という問題もあり、田淵@XLsoft さんの記事にある通り、プラットフォーム依存する場合には Dependency Injection などを使ってうまく吸収してやらねばいけません。と言いますか、

  • プラットフォーム依存しない部分を、PCL で書いて、TDD する。
  • 機種依存/プラットフォーム依存する場合は、各コードで書く(場合によっては Objective-C とか)
  • つなぎの部分を Interface/DI する。

ってな感じの地味な作業がベターかなと今は思ってます。作業量中心に考えて、それぞれのアプリ作成の状況に最適化、ってところですね。プラットフォーム毎のコードに苦労するのであれば、Xamarin.Forms などを使うメリットが低くなってしまうということです。

Reflection を使って、プラットフォームの差分を吸収しようかと思ったのですが、Xamarin.Android/iOS 自身が取っている TypeForwardedToAttribute を使うのもいいかもしれないと考えつつありますが。

■既存のプロファイルを調べる

というわけで、新しいプラットフォームに対応しようとすると、新しいプロファイルに対応しないといけません。それぞれのプラットフォームの組み合わせによって「Profile999」な感じで番号が振ってあるという大混乱ぶりなのですが、ソースコードが github などにアップされていれば(あるいは社内で持っていれば)プロファイルを切り替えてリビルドすれば ok です。

が、ソースコードのプロジェクトがあればよいのですが、DLL しかない状態に陥ると「これは、どのプロファイルなのか?」ってのが問題になる…と思いますよね。DLL を Visual Studio に参照設定しようとすると「異なるプロファイルなので、追加できません」とエラーが出るですが、果たして、その DLL は何のプロファイルなのか?

image

PCL の DLL をバイナリエディタで覗いてみると、プロファイルが書いてあります。これをバイナリで検索してもよいのですが、Assembly.LoadFile した後に、CustomAttributes コレクションの中身を探ってやれば見つかります。型が System.Runtime.Versioning.TargetFrameworkAttribute なものが見つかるので、そこに、”.NETPortable,Version=v4.5,Profile=Profile7″ な書式で書かれています。

■プロファイル名を抽出するツール

https://github.com/moonmile/CheckPCLProfile に、プロファイル名と、対応するプラットフォームを表示させるツールを公開しました。

image

コマンドラインで PCL の DLL を渡すと、プロファイ名が出てきます。たぶん、PCL 以外の DLL を渡すと落ちます。

AssemblyResolve で動的に DLL を読み込ませているのは FSharp.Core.dll のためです。どうやら、F# で作った PCL は CustomAttributes コレクションを探索するときに FSharp.Core を使うみたいですね。大抵の場合、PCL の DLL と同じ場所に FSharp.Core.dll があるので、それも一緒に読み込ませています。

■プロファイル名を直接書き換えると、別プロファイルで読み込めるのか?

実は、これが本題で作ったツールです。バイナリに「Profile7」と書かれている訳だから、「Profile78」と書き換えてしまえば、疑似的に対応する DLL が作れるのではないか?ってことです。名前が長くなってしまうので、文字列長あたりを直さないと駄目なんでしょうが、これを焼てみると…

結論は、だめでした。Assembly.Load 時に例外が発生して落ちてしまいますね。どうせ、制限されたクラスやメソッドしか使っていないのであれば、プロファイル名だけを切り替えればいいような気もするのですが…まぁ、これはリビルドってことで。

カテゴリー: ツール, C#, Xamarin | PCL の DLL からプロファイル名を表示するツールを公開 はコメントを受け付けていません

Xamarin.iOS/Android に対応している Profile78 とは何か?

f# – FS2024 Static linking error when PCL project use by TypeProvider – Stack Overflow
http://stackoverflow.com/questions/25175031/fs2024-static-linking-error-when-pcl-project-use-by-typeprovider

@AshtonKJ さんから解答を貰った、以下のところを、少しメモ書き

Once that is done, you will need to select either Profile78, or Profile259 (I would recommend 78, as the current Xamarin.Forms nuget package doesn’t support 259).

Portable Class Library を作るときに、対応するサブセットがあって、Xamarin.iOS/Android の場合に対応しているのが Profile78 です。Profile 自体は、対応する実行環境のセットがあって、Windows 8.1 やら Windows Phone やらの組み合わせでサブセットの番号が決まっています。対応自体は、C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\ の中にある、SupportedFrameworks フォルダの中身を見ればいいのですが、なかなか手間なので、対応表を作りました。

Profile 対応表 https://onedrive.live.com/view.aspx?resid=1709CDA2BB05E665!22702&ithint=file%2cxlsx&app=Excel&authkey=!ACgy7LWqZ4QjVZ8

私の環境では、Xamarin.iOS/Android が入っているので、これの対応も入っています。プログラム自体は、https://gist.github.com/moonmile/91573c79fed65062a217 にあるので、自分の環境で作ってみてください。

プロファイルの中にある DLL は名前は同じですが、API の実装が異なるので、それぞれの環境にあったプロファイルの DLL を使わないといけません。ですから、プロファイルの中にある API の一覧がほしい…のですが、無いのかな? Leveraging existing code across .NET platforms – .NET Blog – Site Home – MSDN Blogs がその役割をしているような気もするけど。ちょっときちんと調べていない。

■Xamarin.Forms の Mobile Apps で PCL を作る

まず、Mobile Apps で Xamarin.Forms 用の PCL を作ると、これが「Profile78」になっています。

image

ソリューションエクスプローラーで、PCL の「参照設定」→「.NET」を選択した状態でプロパティウィンドウのパスを見ます。

image

ここに「Profile78」が書いてある。

image

プロジェクトのプロパティから「ライブラリ」→「ターゲット」を見ると、どのプラットフォームに対応するのかが出てきます。

image

Xamarin Studio だと、もうちょっと直接的にプロファイルが見れて、プロジェクトのオプションから「ビルド」→「一般」でプロファイルがわかる。

□Profile78 の場合

image

ここの Target Framework をちょいちょいと変えてやると対応するプロファイルの番号がかわります。Xamarin.Forms の場合は、Windows Phone Silverlight が入っているので「Profile78」になっていますが、Silverlight を外すと「Profile7」に変わります。

□Profile7 の場合

image

□Profile259

Profile259 ってのは、Windows Phone 8.1 を含んだ場合ですね。結構制限された環境であることが想像できます。

image

  7 78 259 47
.NET 4.5
SilverLight 5      
Widnows Phone SL8    
Windows 8
Windows 8.1    
Windows Phone 8.1      
Xamarin.Android
Xamarin.iOS

そんな訳で、Xamarin.iOS/Android に対応した PCL を作る場合には、Profile78 か Profile259 を作ればよいわけです。

■F# でポータブルライブラリを作るとどうなるのか?

C# では PCL を作るときやプロジェクトで対応する Profile を指定できるのですが、F# の場合はこれができません。何故できないのかは不思議です。まあ、歴史的経緯で FSharp.Core があちこちに散らばってしまっているためなのかな、と。さまざまな FSHarp.Core は、C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\ にあります。

image

あと、ビルド時には C:\Program Files (x86)\Microsoft SDKs\F#\ を参照していたりします。.NETCore にある 3.78.3.1 とか 3.259.3.1 は、それぞれ Profile78 と Profile259 に対応したプロファイルです。これは、https://visualfsharp.codeplex.com/ からダウンロード&インストールするとできるフォルダです。

Visual F# Tools – Home
https://visualfsharp.codeplex.com/wikipage?title=Current%20Priorities

にある通り、「Put the finishing touches to Profile78 and 259」されます。

プロジェクトを作成するときも、新しいテンプレートが増えて、

  • Portable Library(.NET 4.5 Widnows Store, Windows Phone 8 Silverlight)
    → Profile78 相当
  • Portable Library(.NET 4.5 Widnows Store, Windows 8.1, Windows Phone 8 Silverlight)
    → Profile259 相当

image

になります。

試しに、「Portable Library(.NET 4.5 Widnows Store, Windows Phone 8 Silverlight)」のほうを作って、プロパティのパスを見ると、「C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile78」になっていて、プロジェクトのプロパティではターゲットが「F# 3.1 (FSharp.Core, 3.78.3.1)」になります。このマイナーバージョン部分が、プロファイル番号に相当するという混沌さです(苦笑)。

image

おそらく、FShap.Core を C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\ に突っ込んでしまうか、C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\ 以下、相当のフォルダを C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\.NETPortable\ に作ってしまうほうが素直だと思うのですが…先行きどうなるか分かりません。対応するプロファイルが増えると混沌の度合いが増しそうです。実際、FSharp.Data が動的ロードする FSharp.Core に Profile78 を追加するときは、このマイナーバージョンを見る必要があってで、えらいことになりそうです。

ちなみに

  • ポータブルライブラリ
    → Profile7 .NET 4.5 → FSharp.Core 3.3.1.0
  • ポータブルライブラリ(レガシー)
    → Profile47 .NET 4.0 → FSharp.Core 2.3.5.1

になります。

  Profile FSharp.Core .NET
Portable Library(.NET 4.5 Widnows Store, Windows Phone 8 Silverlight) 78 3.78.3.1 4.5
Portable Library(.NET 4.5 Widnows Store, Windows 8.1, Windows Phone 8 Silverlight) 259 3.259.3.1 4.5
ポータブルライブラリ – Portable Library 7 3.3.1.0 4.5
ポータブルライブラリ(レガシー) – Portable Library Legacy 47 2.3.5.1 4.0
ライブラリ – Library Native 4.3.1.0 4.5

■結論

というわけで、F# で Xamarin.iOS/Android 用のライブラリを作るときに、Profile7 しか選択肢がなかったけど、Profile78 か Profile259 が増えたよ、って話です。Xamarin.Forms を使う時は、Profile78 を使えば OK。

Xamarin.Forms 自体が Windows Phone 8.1 に対応していないので意味はありませんが、Profile259 にしておくことで、Windows Phone 8.1 から参照設定できるようになるので、ライブラリとして利用させることができます。F# のポータブルライブラリ=Profile47 相当が Window Phone 8.1 を含んでいなかったので、これで、Xamarin.iOS/Android, Windows Store 8.1, Windows Phone 8.1 の共通ライブラリが作れるようになった、ってことです。

~~~

FSharp.Core がややこしいことになっているのと、先の TypeProvider の謎の実行エラー(StringStream のコンストラクタがないエラー)も、ここに関わっているような気がするので、Profile 間で呼び出せる API を総ざらいしたほうがよさそうですね。ビルドは通るけど実行エラーになるのはマルチプラットフォーム開発の常なので、もうちょっと突っ込んで調べたほうが安全そうです。

カテゴリー: F#, Xamarin | 1件のコメント

PCL対応のTypeProviderを作…れるのかな?の巻

Xamarin.Forms の TypeProvider を作ろうとしたが断念したの巻 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6104

の続きです。FSharp.Data だと PCL で動いているよん、という話と。 @Reed Copsey, Jr. さんが独自だけど Xamarin.Forms も動いたよ(まだ変なバグで修正中だそうですが)とうことで、週末に FSharp.Data のアプローチを見ていきました。

逃れる手段としては、

f# – FS2024 Static linking error when PCL project use by TypeProvider – Stack Overflow
http://stackoverflow.com/questions/25175031/fs2024-static-linking-error-when-pcl-project-use-by-typeprovider/25194727#25194727

な風にリフレクションを使えばいいのだけど、そもそも Native のライブラリを Android に直接持っていくのも変な話だし、危うい点があるので、できることならば PCL で持っていきたい。そこで、FSharp.Data の XmlProvider のところをちまちまと読み込んだところ、なるほど、

  • TypeProvider でビルドしているときは、Native で動かす。
  • 作成した Type を動かすときは、それぞれの Profile に従う。

という構造になっています。こんな風に、FSharp.Data.DesignTime と FSharp.Data.Profile47 を分けます。DesingTime のほうはフルセットが入っているバージョンで、Visual Studio を使って TypeProvider するときに使うものです。Profile47 のほうは、実行時に呼び出されるもので、それぞれのプロファイルでプロジェクトを作ります。

image

実行時に使う法は。FSharp.Data.dll という名前に統一しておいて、対応する FSharp.Core は動的に読み込むという仕組みですね。なるほど、確かにこれで動きそうです。FSharp.Core の動的読み込みは、AssemblyResolver.fs に書いてあるので、Profile78 と Profile259 を追加します。この2つは、Visual F# Tools で使われるプロファイルで、Profile78 が Xamarin.iOS/Android で使われる PCL になります。プロジェクト自体は、こんな感じで追加されています。

 image

でもって、Profile47 と似た感じで作っては見たのですが、何故かコンソールで動かすと、追加情報:メソッドが見つかりません: ‘Void System.IO.StringReader..ctor(System.String)’ なエラーを吐きます。非常に不思議です。さらに不思議なのは、TypeProvider で作った文字列をそのまま使う場合には出なくて、何か加工しようとすると出るエラーです。

image

また、XmlProvider にある Parse メソッドを使おうとすると、以下のようなエラーが出ます。これも不思議です。

image

このエラーは、Profile47 の場合はでなくて、もともとの FSharp.Data にある Profile7 の場合も出ます。何か変な感じで PCL のロードをしているような気もするのですが、たぶん原因は AssemblyResolver.fs にあるような気がしています。System.IO.StringReader が無いことはないので、対応する System のロードが間違っている感じ。

ひとまず、以下のアプローチをすれば PCL 内で TypeProvider を使えるようになるらしい。

  • TypeProvider のデザイン時の DLL を分けて、動的ロードできるようにする。
  • 実行時のアセンブリは実行環境のプロファイルに合わせて、デザイン時には、DesignTime を動的に読み込む。
  • ビルド時の FSharp.Core は、プロファイルに合わせて動的読み込み?
  • ビルド時に必要なアセンブリは動的読み込みしている? > AssemblyResolver.fs の referencedAssembliesPairs これが足りないような気がする。

実験コードは、こちら
https://github.com/moonmile/FSharp.Data.PCL

カテゴリー: F# | 1件のコメント

meArmPi の動作メモ

F# TypeProvider を PCL 上で動かす試みは、FSharp.Data と同じ方式を取ればよいということが分かったのだが、何故か Profile47 しかうまく動作しない。元の XmlProvider に Profile78 と Profile259 を加えててもうまくいかないし、試しに Profile7 を作っても駄目。何故か System.IO.StringStream のコンストラクタがないという謎なエラーでる…まあ、これはひとまず、冷ましておいて、Raspberry Pi で meArm を動かすことに集中しよう

閑話休題

meArm は、http://www.phenoptix.com/products/mearm-pocket-sized-robot-arm から 29.99 ユーロ(約 3500円ぐらい)で買えるロボットアームです。駆動系は10個3000円で変えるサーボを使っているので、自作できる方はそれで。手っ取り早くアームの動きだけ試したい場合はこれでよいかと。

PCA9685搭載16チャネル PWM/サーボ ドライバー (I2C接続) 経由でサーボを動かすのを忘れずに。meArm 自体は 4チャンネルなので、後12チャンネル動かせますね。手元にプチロボのサーボが余っているので、何か別のものも同時に動かします。あと、Raspberry Pi の Raspberry Pi用T型I/O延長基板 があると工作が楽です。半田付けは苦手なのですが、まあ、ちまちまやればなんとかなるかと。

■プログラムを動かす

サーボを動かすのは何でもいいのですが、Raspberry Pi から制御したいので、https://github.com/RorschachUK/meArmPi にある動作確認用のコードで動かしてみます。

サーボの順番

  • Servo 0: meArm rotating base
  • Servo 1: meArm shoulder (right hand side servo)
  • Servo 2: meArm elbow (left hand side servo)
  • Servo 3: meArm gripper

Raspberry Pi のピンの順番

  • Adafruit GND to RPi GND
  • Adafruit SCL to RPi SCL0
  • Adafruit SDA to RPi SDA0
  • Adafruit VCC to RPi 3.3V
  • Adafruit V+ to RPi 5V

Raspberry pi のピン自体は http://wiringpi.com/pins/ にあります。そのまま拝借

あとは、5V の電源(電池4個で6Vですが)が必要なので、適当な電池ボックスにいれてブレットボードに接続あるいは、先のサーボドライバーの青いところに接続します。GND のほうを電池のマイナスにすればOK(初心者だし…)。

Python で試験動作

まずは、この python コードを C# に移植から。

カテゴリー: RaspberryPi | meArmPi の動作メモ はコメントを受け付けていません