PowerShell で

、

C# で書かれたメソッドの JIT をフックする C++ のライブラリの
コードを自動生成するテンプレートエンジンのための

DSL
を実装してみた
杉浦 彰
Twitter: @urasandesu
Blog: http://urasandesu.blogspot.jp/
自己紹介
自己紹介


名前




杉浦 彰(Akira Sugiura)

職業





SE
画像情報管理のためのソフトウェア開発(C#/WPF)

年齢




今年で 0x20 歳

オンラインでの活動


Twitter: @urasandesu



Blog: http://urasandesu.blogspot.jp/



.NET(CLR) の基礎技術や、開発のための基盤に関心


PE ファイル/メタデータ/IL/厳密な名前/GAC/プロファイラ/AppDomain/CodeDOM/
動的アセンブリ/式木/DLR
自己紹介


オンラインでの活動(続き)


Prig(Prototyping jig)



C# (に限らず、.NET 上で動作する言語)で書かれたプログラムの JIT をフックし
て、実行時に処理を入れ替える。



構想含めると 5 年近く、7 回ほど作り直ししてますが、まだ動きません・・・。





単体テストのためのテストダブル生成フレームワーク。

やっと入れ替え前のメソッドの中身をコピーして、それを使って上書きして、元の
処理がそのまま動くよね、ってところ。

似たようなことができるもの




Microsoft Fakes/Typemock Isolator/Telerik JustMock

今回の PowerShell 勉強会のネタにはなりました!
DSL とは?
DSL とは?



ドメイン特化言語(英: domain-specific programming language)



特定のタスク向けに設計されたコンピュータ言語。



一種類のタスクをうまく実行することに集中。



ポリシー


その領域(ドメイン)をより表現しやすくする。



そのドメインにおいては、仕様変更に対して最小限の記述で済ませられる。



包括的ではない。
DSL とは?


ドメイン特化言語の例


Unix シェルスクリプト


ファイル操作、プログラム実行、テキストの印刷に特化。
bash[~]$ cat tel | sed 's/(...)(....)(....)/1-2-3/'
090-1111-2222
080-9876-9876
090-2222-1111
bash[~]$

※シェルスクリプトをベースにした PowerShell も、DSL の一つ!
PS ~> gc tel | % { $_ -replace '(...)(....)(....)', '$1-$2-$3' }
090-1111-2222
080-9876-9876
090-2222-1111
PS ~>



他にも、SQL や HTML、Wiki のマークダウンなど。
DSL とは?



ドメイン特化言語の対義語としてあるのが、汎用言語
(英: general-purpose programming language)



例
DSL とは?



汎用言語は工具箱


様々なタスク向けのツール群が含まれている。
DSL とは?



ドメイン特化言語は電動ドリル


穴を空けることにかけては強力なツール。

自分の工具箱を見て、もっと良いドリル
が必要だと思ったら、ドメイン特化言語
を探してみたほうが良いかも!
DSL とは?


無かったら?
自作ライブラリ
紹介
自作ライブラリ紹介



PSAnonym





目的問わず利用できる PowerShell 向けの補足的なライブラリ。
公開中: https://github.com/urasandesu/PSAnonym

Swathe.Automation






C# で書かれたメソッドの(略

公開準備中・・・

残念ながら・・・


今のところ PowerShell 2.0 上でしか動きません (>_<)



主にパフォーマンス向上のため、アンドキュメントな API 呼び出しや動的
アセンブリを使った非公開処理呼び出しを多用しているため。
自作ライブラリ紹介



Demo
自作ライブラリ紹介


PSAnonym.Linq


PowerShell で LINQ っぽいものを書けるようにするモジュール。



PowerShell 2.0 でよく上げられる Select-Object の -First スイッチ問題や、
フロー制御構文の外で break を記述すると、スクリプト全体が止まってし
まう問題の解決が動機。



よく使うクエリ(Select/SelectMany/Where/Skip/Take/Any/Zip など)
を中心に、現在 27 クエリを実装。



例
PS ~> QRange 1 10 | QSelect { $1 * $1 } | QToArray
1
4
9
16
25
36
49
64
81
100
自作ライブラリ紹介


PSAnonym.Prototype


PowerShell を、プロトタイプベース&多重継承サポートなオブジェクト指
向言語として使えるようにするモジュール。



PowerShell も DSL の一つ。クラス定義などは本来不要。



テンプレートエンジンに、PowerShell の強力なテキスト処理系はもってこ
い。しかし、実際に使うには、「状態を持ち、自分自身を操作できる」、
「基底 – 派生関係を持ち、生成の対象によって一部の振る舞いを変えるこ
とができる」といったクラスのような存在が欲しくなる。



例
PS ~> $animal1 = Prototype Animal | AbstractMethod Cry
PS ~> $dog1 = $animal1 | Prototype Dog | OverrideMethod Cry { 'わんわん' }
PS ~> $cat1 = $animal1 | Prototype Cat | OverrideMethod Cry { 'にゃーにゃー' }
PS ~> $chimaira1 = $dog1, $cat1 | Prototype Chimaira -Force |
>>
OverrideMethod Cry { $Dog.Cry() + $Cat.Cry() }
PS ~> $animals = $dog1, $cat1, $chimaira1
PS ~> $animals | % { $_.Cry() }
わんわん
にゃーにゃー
わんわんにゃーにゃー
自作ライブラリ紹介


Swathe.Automation


C++ のヘッダーファイルと実装ファイルの依存関係管理を、自動生成によ
る力業で解決するための設定 DSL。
*Fwd.h
Swathe.ps1

依存関係の定義ファイル
*Fwdh.Template.ps1
*h.Template.ps1
*hpp.Template.ps1
*cpp.Template.ps1

テンプレートファイル

*.h
New-xx.ps1

*.hpp
*.cpp

VC++ プロジェクト毎の
テンプレートエンジン

C++ ヘッダー/実装ファイル
xx.vcxproj
xx.vcxproj.filters

VC++ プロジェクトファイル
自作ライブラリ紹介
# ----------------------------------------------------------------------------------------------#
Swathe.ps1
# Declaration
#
Import SimpleHeapProvider
Import SmartHeapProvider
依存関係の定義ファイル
Import PersistableHeapProvider
・・・
Declare $RootDirectory Urasandesu Swathe / Hosting BaseClass / Base HostInfo / Fwd .h
Declare $RootDirectory Urasandesu Swathe / Hosting BaseClass / Base HostInfo / / .h
Declare $RootDirectory Urasandesu Swathe / Hosting BaseClass / Base HostInfo / / .hpp
Declare $RootDirectory Urasandesu Swathe AutoGen Hosting BaseClass / Base HostInfo / / .cpp
・・・
#
# ----------------------------------------------------------------------------------------------# ----------------------------------------------------------------------------------------------#
# Dependency
#
DependsOn BaseHostInfoFwdH DefaultHostInfoApiHolderFwdH
DependsOn BaseHostInfoH HostInfoFacadeH BaseHostInfoFwdH
DependsOn BaseHostInfoHpp BaseHostInfoH
DependsOn BaseHostInfoCpp BaseHostInfoHpp DefaultHostInfoApiHolderH DefaultHostInfoPimplApiHolderH
・・・
#
# -----------------------------------------------------------------------------------------------
自作ライブラリ紹介
*Fwdh.Template.ps1
@"
#pragma once
#ifndef $($Me.IncludeGuard)
#define $($Me.IncludeGuard)
"@ + $({ $Me.DependentHeadersWithoutCommon } | QSelect { @"
#ifndef $($1.IncludeGuard)
#include <$($1.PathWithoutRoot)>
#endif
"@ } | QToParagraphParagraph) + @"

*h.Template.ps1
*hpp.Template.ps1
*cpp.Template.ps1

テンプレートファイル

namespace $($Me.Namespaces[0]) { namespace $($Me.Namespaces[1]) { namespace $($Me.Function) {
template<class ApiHolder>
class $($Me.Name)
{
public:
$($Me.ClassFacadeH.BeginTypedefAlias)
$($Me.ClassFacadeH.DeclareTypedefAlias)
$($Me.ClassFacadeH.EndTypedefAlias)
・・・
};
}}}}

#endif
"@

// namespace $($Me.Namespaces[0]) { namespace $($Me.Namespaces[1]) { namespace $($Me.Function) {

// $($Me.IncludeGuard)
自作ライブラリ紹介
*Fwd.h
*.h
#pragma once
#ifndef URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H
#define URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H
#ifndef URASANDESU_SWATHE_AUTOGEN_HOSTING_CLASSFACADE_HOSTINFOFACADE_H
#include <Urasandesu/Swathe/AutoGen/Hosting/ClassFacade/HostInfoFacade.h>
#endif

#ifndef URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFOFWD_H
#include <Urasandesu/Swathe/Hosting/BaseClass/BaseHostInfoFwd.h>
#endif
namespace Urasandesu { namespace Swathe { namespace Hosting {
template<class ApiHolder>
class BaseHostInfo
{
public:
SWATHE_BEGIN_HOST_INFO_FACADE_TYPEDEF_ALIAS
SWATHE_DECLARE_HOST_INFO_FACADE_TYPEDEF_ALIAS
SWATHE_END_HOST_INFO_FACADE_TYPEDEF_ALIAS
・・・
};
}}}}
#endif

// namespace Urasandesu { namespace Swathe { namespace Hosting {
// URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H

*.hpp
*.cpp

C++ ヘッダー/実装ファイル
自作ライブラリ紹介
xx.vcxproj
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
VC++
・・・
<ItemGroup>
<ClCompile Include="UrasandesuSwatheAutoGenHostingBaseClassBaseHostInfo.cpp" />
・・・
</ItemGroup>
<ItemGroup>
<ClInclude Include="UrasandesuSwatheHostingBaseClassBaseHostInfo.h" />
<ClInclude Include="UrasandesuSwatheHostingBaseClassBaseHostInfo.hpp" />
<ClInclude Include="UrasandesuSwatheHostingBaseClassBaseHostInfoFwd.h" />
・・・
</ItemGroup>
</Project>

xx.vcxproj.filters

プロジェクトファイル
私のケース
私のケース


なぜこんなものを?



Prig は C++ がメイン開発言語。




マネージコードの JIT をフックするので、直接マネージコードを使うことはできない。

最初からある程度の規模を予想。




上記のものについて、生存期間が同じもの達に対して、その管理クラスを・・・。



さらにメモリ管理もある程度グルーピングして、その生成・破棄クラスを・・・。





主なクラス・・・ランタイムを動かすホスト、PE ファイルのリーダー/ライター、ラ
ンタイム情報へのアクセッサ、GAC とのやりとりを行う各種クラス、メタデータ情
報を表す各種クラス(Assembly/Module/Type/Method/Field/Property/Event
…)、プロファイリング情報を表す各種クラス、厳密な名前を扱うための各種クラ
ス、・・・。

あれ?1,000 クラスとか普通に超えるんじゃね?

ヘッダーファイルと実装ファイル間の依存関係をうまく管理できないと詰む!
私のケース


C++ の依存関係のおさらい


コンパイル単位につき、





宣言はいくつでも
定義は 1 つ

#include の順番に依存
するのはまずい


インクルードガード

✔一意になるよう
に!
ファイルパスや名前空間から生成する
のが良いみたい

#pragma once
#ifndef URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H
#define URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H
#ifndef URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFOFWD_H
#include <Urasandesu/Swathe/Hosting/BaseClass/BaseHostInfoFwd.h>
#endif
namespace Urasandesu { namespace Swathe { namespace Hosting {
template<class ApiHolder>
class BaseHostInfo
{
・・・
};
}}}}
#endif

// namespace Urasandesu { namespace Swathe { namespace Hosting {
// URASANDESU_SWATHE_HOSTING_BASECLASS_BASEHOSTINFO_H
私のケース



簡単にヘッダーファイル/実装ファイル間の依存関係管理できるツールって無い?


Microsoft :Visual C++ 開発用ツール/依存関係グラフ





C コードおよび C++ コードに対する依存関係グラフの生成機能!
Visual Studio Ultimate が必要。個人で使うには

・・・(´・ω・`)

GNU project: gcc





-MD スイッチで、依存関係一覧を make 向けのフォーマットで出力可能!
変更しても書き戻せない

・・・(´・ω・`)

Eclipse : C/C++ Development Tooling (CDT)


今調べると、Java の “Organize Imports” みたいな感じで、”Organize Includes”
ができるようになってる!?



うまく動かせず

・・・(´・ω・`)
私のケース



依存関係の管理が必要になるところは、汎用テンプレートエンジンを使って
自動生成で同期するしかない・・・


Microsoft:T4(Text Template Transformation Toolkit)





Visual Studio で使えるテンプレートエンジン。
C#/Visual Basic 向け。C++ は

Apache: Velocity


Java の汎用テンプレートエンジン。業務でも使ったことあり。





・・・(´・ω・`)

.NET 向けのプロジェクトに Java は

・・・(´・ω・`)

Microsoft:Razor


元々は ASP.NET MVC の View エンジンとして開発されたテンプレートエンジン。



Matthew Abbott 氏を中心に、普通のライブラリのように使えるもの(RazorEngine)が
開発されている。

RazorEngine、今見直すとこれで全然良い!ヾ(゚∀゚)ノ
当時は、「ASP.NET」の時点で候補から外れてたみたい・・・
私のケース


無かった (`・ω・´)キリッ

(´-`).oO(見つけられなかったとも言う・・・)
PowerShell と
DSL
PowerShell と DSL


PowerShell インアクション


Soul of a New Language



言語デザイナーの一人でもある
Bruce Payette が解説。



ポリシーや、なぜこうなってるの
かが記載してあり納得感。



第8章 スクリプトブロックとオブ
ジェクト には、「メタプログラミ
ング」や「言語の拡張」といった
心躍る内容がヾ(゚∀゚)ノ
PowerShell と DSL



知っておくと良いかも?キーワード


解析モード



クロージャ



Invoke-Expression



PSCustomObject



パイプライン



PSObject



スクリプトブロック



PSMemberInfo



スコープ



Runspace



モジュール



AppDomain



types.ps1xml
終わりに
終わりに
終わりに
終わりに
参考文献
参考文献


ドメイン固有言語 - Wikipedia
http://ja.wikipedia.org/wiki/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E5%9B%BA%E6%9C%89%E8%A8%80%E8%AA%9E



USP友の会:第3回シェル芸爆破デスマッチ勉強会&第21回餃子爆破定例会やってきた
http://www.usptomo.com/PAGE=20130217USPSTUDY



「第3回シェル芸爆破デスマッチ勉強会&第21回餃子爆破定例会やってきた」をPowershellでやってみた - tech.guitarrapc.com
http://tech.guitarrapc.com/entry/2013/02/18/070226



#include <rules> | What your mother never told you about graphics development
http://zeuxcg.org/2010/11/15/include-rules/



依存関係グラフでのコード依存関係の視覚化 - MSDN
http://msdn.microsoft.com/ja-jp/library/dd409453(v=vs.110).aspx



GCC, the GNU Compiler Collection - GNU Project - Free Software Foundation (FSF)
http://gcc.gnu.org/



Antaris/RazorEngine – GitHub
https://github.com/Antaris/RazorEngine



RazorEngineがすごく便利 (System.Web.Razorのラッパーライブラリ, テンプレートエンジン, Razor記法) - いろいろ備忘録日記
http://d.hatena.ne.jp/gsf_zero1/20121030/p1



Organize Includes Comes to CDT
http://www.eclipse.org/community/eclipse_newsletter/2013/october/article3.php
ご清聴
ありがとうございました!

Power shell で DSL