PowershellでAsyncCallbackを使うにはどうしたらよいか
PowershellでUdp双方向通信を行うスクリプトを書こうとしてハマった
前提
メインスレッド Udp送信担当
サブスレッド Udp受信担当
受信はメインスレッド内でUdpClient.BeginReceiveを呼び出して非同期処理にする。
コールバック関数を定義し、 Receiveが値を受け取った時に関数呼び出し(受信データを基にメインスレッドのフォーム更新や結果のUdp送信など)を実行して、再びUdpClient.BeginReceiveを呼ぶ
いわゆるAPM(Asynchronous Programming Model)ってやつです
スクリプト
受信開始とコールバックのところのみ
(略) # UDP受信待ち受け function udpreceiver_start { param( $ipaddress = "127.0.0.1", #バインドするアドレス $port = 4444 #バインドするポート ) # ローカルエンドポイント定義 $localEP = new-object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($ipaddress),$port) # リモートエンドポイント定義 #$remoteEP = new-Object System.Net.IPEndPoint([System.Net.IPAddress]::Any, 0); # UDPサーバーを生成 $udprecv = new-Object System.Net.Sockets.UdpClient($localEP) # 非同期的なデータ受信を開始する Write-Host "BeginReceive!" $handle = $udprecv.BeginReceive([System.AsyncCallback]${function:udpreceiver_callback},$udprecv); } # UDP受信callback function udpreceiver_callback { param( $ar ) Write-host "Callback!!" $udprecv = [System.Net.Sockets.UdpClient]{$ar.AsyncState}; # 非同期受信を終了する [System.Net.IPEndPoint]$remoteEP = $null; try{ $buf = $udprecv.EndReceive($ar,[ref]$remoteEP); } catch [System.Net.Sockets.SocketException] { Write-Host ("受信エラー({0}/{1})" -f $_.Message, $_.ErrorCode) return; } catch [ObjectDisposedException] { //すでに閉じている時は終了 Write-Host "Socketは閉じられています。" return; } $str = [System.TexT.Encoding]::UTF8.GetString($buf) Write-host "データを受信しました" Write-Host ("受信したデータ:{0}" -f $str) Write-Host ("送信元アドレス:{0}/ポート番号:{1}" -f $remoteEP.Address, $remoteEP.Port) //再びデータ受信を開始する $udprecv.BeginReceive([System.AsyncCallback]${function:udpreceiver_callback},$udprecv); } (略) udpreceiver_start (略)
問題点
UDPでデータを受信すると・・・
_人人 人人_
> 突然の死 <
 ̄YYYY ̄
原因分析1
コールバックとして普通の関数を[System.AsyncCallback]
でキャストしたものを渡したのが原因だろうかと思って、そこをC#で書いて渡してみる。
(略) # UDP受信待ち受け function udpreceiver_start { param( $ipaddress = "127.0.0.1", #バインドするアドレス $port = 4444 #バインドするポート ) # ローカルエンドポイント定義 $localEP = new-object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($ipaddress),$port) # リモートエンドポイント定義(空:後で使う) $remoteEP = new-Object System.Net.IPEndPoint([System.Net.IPAddress]::Any, 0); # UDPサーバーを生成 $udprecv = new-Object System.Net.Sockets.UdpClient($localEP) # 非同期的なデータ受信を開始する #無理だった Write-Host "BeginReceive!" #$handle = $udprecv.BeginReceive([System.AsyncCallback]${function:udpreceiver_callback},$udprecv); $handle = $udprecv.BeginReceive([Udp]::udpreceiver_callback,$udprecv); } Add-type @" using System; using System.Net; using System.Net.Sockets; using System.Text; public static class Udp { public static void udpreceiver_callback(IAsyncResult ar=null) { Console.WriteLine("Callback!"); var udprecv = (System.Net.Sockets.UdpClient)ar.AsyncState; byte[] buf; // 非同期受信を終了する var remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0); try { buf = udprecv.EndReceive(ar, ref remoteEP); } catch(System.Net.Sockets.SocketException ex) { Console.WriteLine("受信エラー({0}/{1})",ex.Message, ex.ErrorCode); return; } catch(ObjectDisposedException) { //すでに閉じている時は終了 Console.WriteLine("Socketは閉じられています。"); return; } var str = System.Text.Encoding.UTF8.GetString(buf); Console.WriteLine("データを受信しました"); Console.WriteLine("受信したデータ:{0}", str); Console.WriteLine("送信元アドレス:{0}/ポート番号:{1}", remoteEP.Address, remoteEP.Port); //再びデータ受信を開始する Console.WriteLine("BeginReceive!"); udprecv.BeginReceive(udpreceiver_callback, udprecv); } } "@ (略) udpreceiver_start (略)
・
・
・
・
_人人 人人_
> 突然の死 <
 ̄YYYY ̄
ダメでした
対応策
全部C#で書く(爆)
(略) Add-type @" using System; using System.Net; using System.Net.Sockets; using System.Text; public static class Udp { public static void udpreceiver_start( //AsyncCallback callback, string ipaddress = "127.0.0.1", //バインドするアドレス int port = 4445 //バインドするポート ) { //ローカルエンドポイント定義 var localEP = new System.Net.IPEndPoint(System.Net.IPAddress.Parse(ipaddress), port); //リモートエンドポイント定義(空:後で使う) var remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0); //UDPサーバーを生成 var udpClient = new System.Net.Sockets.UdpClient(localEP); //非同期的なデータ受信を開始する Console.WriteLine("BeginReceive!"); //BeginReceive2(callback, udpClient); udpClient.BeginReceive(udpreceiver_callback, udpClient); } public static void udpreceiver_callback(IAsyncResult ar=null) { Console.WriteLine("Callback!"); var udprecv = (System.Net.Sockets.UdpClient)ar.AsyncState; byte[] buf; // 非同期受信を終了する var remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0); try { buf = udprecv.EndReceive(ar, ref remoteEP); } catch(System.Net.Sockets.SocketException ex) { Console.WriteLine("受信エラー({0}/{1})",ex.Message, ex.ErrorCode); return; } catch(ObjectDisposedException) { //すでに閉じている時は終了 Console.WriteLine("Socketは閉じられています。"); return; } var str = System.Text.Encoding.UTF8.GetString(buf); Console.WriteLine("データを受信しました"); Console.WriteLine("受信したデータ:{0}", str); Console.WriteLine("送信元アドレス:{0}/ポート番号:{1}", remoteEP.Address, remoteEP.Port); //再びデータ受信を開始する udprecv.BeginReceive(udpreceiver_callback, udprecv); } } "@ (略) [Udp]::udpreceiver_start() (略)
これだとうまくいきました。
改良版
PowershellのUdpClientではBeginReceive()
メソッドを呼び出さず、C#で記述したstaticメソッド[Udp]::BeginReceive
の中で呼び出すようにすると、
問題点1を解決しつつ、startのメソッドをpowershellにできます。
(略) # UDP受信待ち受け function udpreceiver_start { param( $ipaddress = "127.0.0.1", #バインドするアドレス $port = 4445 #バインドするポート ) # ローカルエンドポイント定義 $localEP = new-object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($ipaddress),$port) # リモートエンドポイント定義(空:後で使う) $remoteEP = new-Object System.Net.IPEndPoint([System.Net.IPAddress]::Any, 0); # UDPサーバーを生成 $udprecv = new-Object System.Net.Sockets.UdpClient($localEP) # 非同期的なデータ受信を開始する Write-Host "BeginReceive!" #$handle = $udprecv.BeginReceive([System.AsyncCallback]${function:udpreceiver_callback},$udprecv); $handle = [UDP]::BeginReceive($null,$udprecv) } Add-type @" using System; using System.Net; using System.Net.Sockets; using System.Text; public static class Udp { public static void udpreceiver_callback(IAsyncResult ar) { Console.WriteLine("Callback!"); var udprecv = (System.Net.Sockets.UdpClient)ar.AsyncState; byte[] buf; // 非同期受信を終了する var remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0); try { buf = udprecv.EndReceive(ar, ref remoteEP); } catch(System.Net.Sockets.SocketException ex) { Console.WriteLine("受信エラー({0}/{1})",ex.Message, ex.ErrorCode); return; } catch(ObjectDisposedException) { //すでに閉じている時は終了 Console.WriteLine("Socketは閉じられています。"); return; } var str = System.Text.Encoding.UTF8.GetString(buf); Console.WriteLine("データを受信しました"); Console.WriteLine("受信したデータ:{0}", str); Console.WriteLine("送信元アドレス:{0}/ポート番号:{1}", remoteEP.Address, remoteEP.Port); //再びデータ受信を開始する udprecv.BeginReceive(udpreceiver_callback, udprecv); } // UdpClient.BeginReceive(callback)を呼び出す関数 public static System.IAsyncResult BeginReceive( Action<IAsyncResult> callback, System.Net.Sockets.UdpClient udpClient ) { return udpClient.BeginReceive( _ => {udpreceiver_callback(_);}, udpClient); } } "@ udpreceiver_start
ちなみにpowershellで書いた関数をコールバック内で呼ぶだけで死ぬので、コールバックはC#で書くしかなかった。どうしようもなかった。
参考にしたリンク先
参照渡しについてのメモ
-C++の参照渡し wandbox.org
-C#の参照渡し wandbox.org
.dotfilesに.emacs.dを結合する
はじめに
前略
無知な私は.dotfilesと.emacs.dを別々のリポジトリで管理していたので
いい加減に統合したいと思います
早々
いろいろ調べて得られた知見(セオリー)としては
1.別ブランチに移動してコピー
2.masterにマージ
という様にブランチを一手間経由するのが確実で良いようです
1.別ブランチに移動してコピー
Emacsブランチの中身になるリモートリポジトリを登録する.
.dotfiles
ディレクトリに移動し,emacsリモートリポジトリを新しいリモート先として登録
$ git remote add Emacs git@github.com:hightoro/emacs
確認
$ git remote -v
フェッチして,新しいブランチを作成する
Emacsブランチに中身をぶちまける
$ git fetch Emacs $ git checkout -b Emacs Emacs/master
これで,ブランチが切り替わり,ディレクトリの中身がemacs.gitのものになります
確認
$ git branch -a
2.masterにマージする
git filter-branch
を使用してサブディレクトリ.emacs.dを作成
すこしだけこのコマンドの解説を読みました
git filter-branch
はコミット履歴に大幅な改変を加えたいときに使用するコマンドだそうです
gitではコミット履歴は(他のプロジェクトメンバに公開して供するまでの間は)
自由に改変すべきだという思想があるそうです
そのためにgit rebase
や各種オプションが用意されているわけです
そしてこのgit filter-branch
はその中でも「複数のコミットに対して,
ある種の改変を行いたい」場合に使用される最終兵器だそうです
今回はgit filter-branch --tree-filter
を使用します
git filter-branch --tree-filter
はこれまでの既存のコミット履歴に対して,
過去のある時点に何らかの操作を適応し、そこから現在までのコミット履歴を一括して改変するものらしいです
ではこれまでの./で行ったコミットをすべてサブディレクトリ.emacs.d/の中で起こったことにしましょう
$ git filter-branch -f --tree-filter "mkdir .emacs && git mv -k {,.[\!.],..[\!.]}* .emacs.d/"
mkdirした.emacs.dの中にぶちまけた中身をすべて放りこんだ上で,これまでにコミット履歴の一部が改変されます
Emacsブランチをmasterブランチにマージ
最後にEamcsブランチをmasterブランチにマージします
$ git checkout master $ git merge Eamcs
これで.emacs.dサブディレクトリにリモートリポジトリが追加され,
コミット履歴にもリモートリポジトリemacsのこれまでのコミット履歴がマージされる
参考:
cygwin上に環境構築
はじめに
ただのメモです
LinuxMintで構築してきたterminal環境をcygwin上に構築したかった
セットアップ
64bit版のsetup.exeを公式よりダウンロード
起動して進めて行く
パッケージのアクセスサーバーはなんとなくIIJさんのサーバーを選択
インストールする
ただしapt-cygに必要なものだけ
apt-cygをダウンロード
公式ではなく、forkしたものをダウンロード(公式の更新は止まってる?)
https://github.com/kou1okada/apt-cyg
普通にブラウザ上からダウンロード
これをCygwinインストールフォルダ内の
/usr/local/bin
に配置
(firefoxでDLするとapt-cyg.txt
になってしまったのでrename)
cygwin上で
$ chmod 755 /usr/local/bin/apt-cyg
これでapt-cygが動く
その後、apt-cygのアクセス先をIIJのサーバに変更する
$ apt-cyg -m ftp://ftp.iij.ad.jp/pub/cygwin/ update
よし! gitを落とそう!
$ apt-cyg install git
git submodule
git-submoduleを使うには、gettexのライブラリが必要
と言う事でapt-cygでインストールしましょう
$ apt-cyg install gettext
これでgit submodule init
などが正常に動くようになります
今回は以上
参考:
http://dqn.sakusakutto.jp/2013/12/64bit_cygwin_apt-cyg.html
LinuxMint17(MATE版)をVirutalboxに導入する際のメモ1
インストール
指示に従って、ポチポチとクリック。virtualboxの設定などは割愛
(スナップショット) (忘れた)
更新
おなじみのコマンド
$ sudo apt-get update $ sudo apt-get dist-upgrade
結構待った。
再起動
(スナップショット)
Guest Additional
必要なパッケージを入れる
$ sudo apt-get install build-essential module-assistant
kernel ヘッダファイルへのシンボリックリンク作成
$ sudo m-a prepare
GuestAdditionalを起動してコンパイル
$ sudo ./VBoxLinuxAdditions.run
再起動 (スナップショット)
日本語化
ibus-mozcよりもfcitx-mozcを薦めているサイト様があったので、試しに
im-configをインストールする必要があると思ったけど、METAには最初から入っていた
$ sudo apt-get install im-config
「Fcitx」と「Mozc」をインストール
$ sudo apt-get install fcitx fcitx-mozc fcitx-frontend-gtk2 fcitx-frontend-gtk3 fcitx-frontend-qt4 fcitx-frontend-qt5 fcitx-ui-classic fcitx-config-gtk mozc-utils-gui
Fcitx を有効に。
$ im-config -n fcitx
(再起動)
(スナップショット)
参考:
http://linuxmintjp.jimdo.com/%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%8C%96/#.U47WdJWKCUk
http://www.ice.is.kit.ac.jp/~umehara/misc/comp/20110723.html
GADT記法+tagをC++で
GADTとは
Generalized Algebraic DataTypes(GADT)と言うHaskellの記法(?)を知りました
Haskellの入門書などで出てくるMaybe型は以下のような定義になっています
data Maybe a = Nothing | Just a
これをGADTで書くと以下のようになるそうです
data Maybe a where Nothing :: Maybe a Just :: a -> Maybe a
GADTで面白いのは、値コンストラクタがただの関数へと変化し、
戻り値型のMaybe型が直和型ではなくなっている(ように見える)ところです
またGADTの本来の特徴は多相型における値コンストラクタ(や一部の関数)
の戻り値型の取る型引数を任意の型に固定できる点です
先ほどのMaybe型を改造します
改造箇所は2型引数を取り、第1引数ではコンストラクタ毎の
識別用の型(いわゆる型tag)を付けるようにできます
--tag型 data NothingT data JustT --改・Maybe型 data Maybe' t a where Nothing :: Maybe' NothingT a Just :: a -> Maybe' JustT a
こんな事ができると何がうれしいのでしょう?
まずパターンマッチを考えます
通常のMaybe型では以下のようになります
func :: (Show a) => Maybe a -> String func (Just x) = "just " ++ show x func Nothing = "Nothing"
これがMaybe'型では以下のようになります
func :: (Show a) => Maybe' tag a -> String func (Just x) = "Just " ++ show x func Nothing = "Nothing!"
あれ?変わってなくね?
と、思いがちになるのですが
Maybe'型であればWarningを出さずに
pattern matchを制限する事ができるようになります
例えば
func :: (Show a) => Maybe' t a -> String func (Just x) = "Just " ++ show x --func Nothing = "Nothing!"
のようにNothingをコメントアウトします
するとGHCでは
prog.hs:11:1: Warning: Pattern match(es) are non-exhaustive In an equation for ‘func’: Patterns not matched: Nothing'
のように警告がでて怒られます
引数のMaybe型がJust aなのかNothingなのかはMaybe型の引数で判別できないため
Nothingで生成したMaybe型を関数に渡すところまでは
コンパイル時にチェックして問題なく通る(※)
→実行時に死ぬと言う事になります
となるとpattern matchにない値コンストラクタは
コンパイル時に検出して欲しいという要求がありそうです
GADTではこれを実現できます
先ほどの関数を以下のように書き換えます
func :: (Show a) => Maybe' JustT a -> String func (Just x) = "Just " ++ show x --func Nothing = "Nothing!"
何処が変わったかというと、1行目のMaybe' t a
がMaybe' JustT a
に変わっています
これでWarningが出なくなります。
(この状態でコメントアウトを解除してもNothingが型エラーになります)
このようにパターンマッチ時にそもそも選択したくないマッチ条件を
排除するために利用されるようです
- (※)ちなみにMaybe型はもともとshow Nothingでaの型が判別できないので
(こちらの意図とは異なるものの)ちゃんとコンパイルエラーになります
(Phantom-typeというコンパイル時型チェックの記法のようです)
本題
このGADT記法+tagのMaybeをC++で再現してみます
{-#LANGUAGE GADTs #-} --tag data NothingT data JustT -- data Maybe data Maybe' t a where Nothing :: Maybe' NothingT a Just :: a -> Maybe' JustT a -- instance class show instance (Show a) => Maybe' tag a where show (Just x) = "Just " ++ show x show Nothing = "Nothing!" main = do --putStrLn $ show Nothing putStrLn $ show (Just 42)
#include<iostream> // tag struct NothingT{}; struct JustT{}; // data Maybe template<typename TAG,typename A> struct Maybe { A _; Maybe(A a):_(a){} }; template<typename T> auto Just(T a){ return Maybe<JustT,T>(a); } template<typename T=int> // 匿名型のままにはできない・・・ auto Nothing(){ return Maybe<NothingT,T>(0); } // instance class show template<typename T> std::string show(Maybe<JustT,T> a) {return "Just "+std::to_string(a._);} template<typename T> std::string show(Maybe<NothingT,T>) {static_assert(std::is_same<T,void>::value,"can't show Nothing"); return "";} int main() { //show( Nothing() ); show( Just(1) ); }
http://melpon.org/wandbox/permlink/IhrmT6PWKLYN424T
参考:
http://faithandbrave.hateblo.jp/entry/20111201/1322718742