先駆者がいた。。

発想が私の作ったTrainNavi2と全く同じでした。

http://rokujo.esy.es/TrainNavi2/index.html

こいつは去年、覚えたてのJavaScript+PHP+MySQLのプログラミング練習として作ったものです。

鉄道Nowのすごいところ

・路線データが豊富(全国!)

・インターフェースが分かりやすい

・スマホ対応

・リアルタイム性に特化しているが、現在時刻の切り替えも可能

 

私の作った方はインターフェースが分かりづらい・複数の線路を並べると見づらい・大画面でないと辛いなど致命的な欠点がありますが、ダイヤの視覚化を目指したので、任意の速度設定ができることだけが強みです。自分で作っておいてなんですが、小田急線で特急が急行を追い抜いていく様子、ラッシュ時の新百合ヶ丘~向ケ丘遊園の電車のノロさ、それと比較した登戸以降の複々線区間の快適さ、小田急と並列させたときの京王の電車の詰まりっぷりの酷さを30~60倍速で眺めるのはとても楽しいです。でも鉄道Nowでも簡単に実装できそうではあります。

いつか鉄道Nowのように実用的なWebサービスを作ってみたいものです。


C++で書かれたDLLにC#からポインタを渡す

C++で書かれたDLLにポインタを渡したいことがあります。例えば次のような関数がエクスポートされていたとします。

void WINAPI ConvertToShort(char* pstr, short* pret);

これをC#側から使用したい。char* は文字列なので C# 側からは string を渡してやるだけでよいですが、short* については何を渡していいものやら困ります。

C#には IntPtr という型があります。これは汎用的なポインタを表す型で、ほぼ void* と同義です。

ただしC#は超厳しい型付け言語なので、void* みたいな万能選手は万能ゆえの曖昧さを解決するために、回りくどい変換メソッドを経由しないと使えません。

具体的には、IntPtrの変数に Marshal.AllocHGlobalで必要なサイズのメモリを確保し、それをC++のDLLに渡します。

さらにMarshal.ReadInt16(必要な型によって異なる)などで変換後、確保したメモリをMarshal.FreeHGlobalで解放する、と3段階の面倒なプロセスを経なければいけません。

C#はその厳しさゆえテキトーなC++とは違いメモリ破壊など厄介な問題が発生しませんが、こういうときに手続きが面倒です。リスクと面倒さはトレードオフですね。

//usingではこれが必要
using System.Runtime.InteropServices;
//ここはクラス内で定義
[DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")]
extern static void ConvertToShort(string pstr, IntPtr pret);
//ここは関数内の記述
IntPtr buffer = new IntPtr();
buffer = Marshal.AllocHGlobal(2); //2バイトのメモリ確保
ConvertToShort("XXXXXX", buffer);
short sval = Marshal.ReadInt16(buffer); //変換
Marshal.FreeHGlobal(buffer); //メモリ解放

実用的には、次のようにConvertToShortをラップして使いやすい形に整えるのが賢明でしょう。

[DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")]
extern static void _ConvertToShort(string pstr, IntPtr pret); //呼び出し元の名前変えちゃう
short ConvertToShort(string str)
{
IntPtr buffer = new IntPtr();
buffer = Marshal.AllocHGlobal(2);
_ConvertToShort(str, buffer);
short sval = Marshal.ReadInt16(buffer);
Marshal.FreeHGlobal(buffer);
return sval;
}

IntPtrには何でも入りますから、例えば構造体をゲットすることも可能です。ただしC#側で構造体をC#流に定義してあげなければなりません。若干面倒です。

参考
IntPtr からの色々な型への変換 – Kenrowの覚書と日々
@IT:.NET TIPS Win32 APIやDLL関数に構造体を渡すには? – C#


C++で作るDLLにはDEFファイルをつけるべき

DLLファイル関連でハマりました。C++で作ったDLLで、

_declspec(dllexport)

を使ってエクスポートした関数をC#側から読み込もうとしても、うまくいきません。「エントリ ポイントがありません」と言われて動きません。原因を調べたところ、dllexportの仕様で、名前に余計な文字列を付けるからだそうです。

例えば、SetDataという関数をDLLに

__declspec(dllexport) void SetData(unsigned char* data)

と登録したとします。Dependency Walkerを使ってDLLを開くと

?SetData@@YAXPAEH@Z

という意味の分からない名前に変化してしまいます!!

C++アプリケーション同士のインポート、エクスポートなら問題ありません。呼び出し側が_declspec(dllimport) を使ってインポートすれば、わけわかめな名前を自動的に解決してくれます。しかしVBやC#などの外部アプリケーションからC++のDLLを使うなら、もともとのメソッド名(SetData)が公開されていないため、エントリポイントがないよ、と文句を言われてしまうわけです。

そこで、DEFファイル(モジュール定義ファイル)が必須となります。外部アプリケーションに対して、これこれのメソッドを公開しますよ、ということを明示するファイルです。

LIBRARY    “TestLib”

EXPORTS
    SetData

内容はこれだけでOKです。VC++を使っているなら、新規作成する項目の中にDEFファイルが存在するはずですので、EXPORTS以下だけを設定すればよいです。

これで、再度Dependency WalkerでDLLの内容を覗くと、見事 SetData というメソッドが公開されていることがわかります。

外部へのメソッドの公開はCOMで行うという手もありますが、COMはオブジェクトを作成することが前提の仕組みですので、目的によって使い分ける必要がありますね。

 

参考

DLLコンパイル時の注意点

COM (Component Object Model)


jQuery: 『getter→引数なし、setter→引数あり』の思想

learn.jquery.com

ふつうのオブジェクト指向なプログラミング言語に馴染んだ人間にとって、次のコードは直感に反します。

// This doesn't work; text() returns a string, not the jQuery object:
$( this ).find( "li a" ).eq( 2 ).text().replace( "foo", "bar" );
// This works:
var thirdLink = $( this ).find( "li a" ).eq( 2 );
var linkText = thirdLink.text().replace( "foo", "bar" );
thirdLink.text( linkText );

1行目のコードが動かないことはまあわかります。問題は4, 5行目です。

thirdLink.text();
がgetterで、”li a” でセレクトされる要素の3番目に入っている文字列を返す。

thirdLink.text( linkText );
はsetterで、”li a”でセレクトされる要素の3番目、にlinkTextの内容をセットする。戻り値は謎。

ややこしい。なんで引数の数によって動作を変えるの!!??すっごく不自然です。

var str = thirdLink.text;
str.replace( "foo", "bar" )
thirdLink.text = str;

のような書き方ならとても分かりやすいけれど、javascriptの仕様でそれはできません。javascriptではgetter, setterの中身を定義することができないので、textオブジェクトの内容を書き換えてしまうだけで終わりです。ならば

var str = thirdLink.text.get();
str.replace( "foo", "bar" );
thirdLink.text.set(str);

としてくれた方が曖昧さが無くてうれしいです。これなら実装も可能です。

しかしjQueryの思想は “Write Less, Do More.” です。開発陣はget() や set()という冗長な記法を許さなかったわけです。
なので、極限まで記述を切り詰めた方法――それが『getter→引数なし、setter→引数あり』という記法に繋がったとのだと理解しました。
jQueryを使うなら暗記は必須です。


詰碁印刷(SGFPrint)4 WindowsのファイルシステムはSJISだった!?CSSで改ページ・印刷時だけ表示したくないアイテムの設定

詰碁印刷3 碁盤と碁石と手順の描画、javascriptで配列の簡単なシャローコピーの作り方 – 六帖のかたすみ

の続きです。

f:id:happyholiday:20150705115440p:plain

ようやく公開できるレベルになったのでwebサイトにアップロードします。

六帖webアプリ

kickzone/SGFPrint · GitHub

今日一番苦労したのは、zipファイル内のファイル名が文字化けしてしまったことです。firebug内でも化けて表示されていました。

javascriptで扱う文字列はそのスクリプトやHTMLを保存した文字コードに依存します。UNICODEでないと中国語やフランス語のセディーユ・アクサン記号やドイツ語のウムラウトが文字化けするし、Apacheサーバーも全てUNICODEで動作しているので、作業はすべてUNICODEで行っています。基本的にはサイト上の文字が全てUNICODEで書かれていれば問題は起きません。

zipファイルの中身のファイル名をデバッガ上で見るとファイル名をゲットしたその場で文字化けしています。どうも、Windowsのファイルシステム自体がSJISでできているようです。ちょっと時代遅れじゃありませんか。

javascriptオンリーで作成していたのでPHPのようなラクラク文字コード変換システム(mb_convert_encodingで一発です)はありません。探していると

JavaScriptで文字コード変換ライブラリ作ってみた | 圧縮電子どうのこうの

無償でライブラリを作っている人がいらっしゃいました!!!ありがたやありがたや。

            var fileStr = fileList[i];
            var fileArr = Encoding.stringToCode(fileList[i]);
            if(Encoding.detect(fileArr, ‘SJIS’))
            {
                var utf8Arr = Encoding.convert(fileArr, ‘UNICODE’, ‘SJIS’);
                fileStr = Encoding.codeToString(utf8Arr);
            } 

こんな感じでたった数行でSJIS→UNICODEへの変換が可能になり、おかげさまで「TOM死活初級001」などの文字列が無事表示できるようになりました。JavaScriptでファイル名を扱うプログラムを書いている方はくれぐれも文字コードに気を付けてください。

詰碁の二段組表示はごく最近に読んだこの本が役に立ちました。罫線の引き方を始めとするレイアウト作成もこの本で習ったことにアレンジを加えるだけでできました。

また、DOMの動的生成も最近読んだjQuery公式マニュアルのおかげで楽々です。canvasだってどんどん配置できます。勉強したことは欠片も無駄にならないものなのですね。

    //ボックス要素を作成 divの中に碁盤とコメントを配置
    var $newBox = $(“<div/>”);
    $newBox.addClass(“box”);
    //タイトル
    var $name = $(“<p/>”);
    $name.addClass(“name”);
    var nameText = element.fileName.split(“.”)[0];
    if(eIndex == -1){
        nameText += ” 問題”;
    }else{
        var no = eIndex+1;
        nameText += ” ” + no.toString() + “図”;
    }
    $name.text(nameText)
    $newBox.append($name);

なお、jQueryが便利だからといって直接CSSプロパティを .css() で設定して見た目をいじってははいけません。昨日書いた理由によるものです。上のコードのように、addClassでクラスをつけ、後のことはCSSファイルに任せましょう。

CSSでは印刷特有の設定を行うことができます。印刷時に表示したくないアイテムには @media print を使って display: none; と設定してやれば、印刷時だけそのアイテムが表示されなくなります。

@media print{
    .no-print{
        display: none; /*印刷時非表示*/
    }
}

これで class=”no-print” と指定したアイテムは印刷時のみ消えます。便利ですね。

@media printとは、印刷時だけ適用する設定ということです。他にも例えばmobileと指定すればスマホや携帯電話のときだけ適用する設定を作ることができます。

最後に改ページです。これは page-break-after: always; を使います。

.subBox{
    height: 1140px;
    margin: 0px 0px 20px 0px;
    padding: 0px;
    page-break-after: always; /* 改ページさせる */
}

私は詰碁6個を1単位として <div class=”subBox> で囲み、6個ごとに改ページさせることに成功しました。

今日でほぼ目的の機能を実装し終えることができました。明日にでもセブンイレブンにPDFファイルを持っていって印刷できそうです。SGFファイルは中国のものが多いので解説が中国語なのは仕方がないのですが、これでは不便なのでいずれは翻訳することも考えています。囲碁用語を全然知らないので苦しいですが。。


jQuery: CSSプロパティのセットは非推奨!?

jQueryの公式ガイドを読んでいたらちょっとびっくりする記述がありました。

 

// Setting CSS properties.
 
$( "h1" ).css( "fontSize", "100px" ); // Setting an individual property.
 
// Setting multiple properties.
$( "h1" ).css({
fontSize: "100px",
color: "red"
});

 jQueryではこんな風に簡単にCSSのプロパティを変更して表示を変えることができるのだけど、お勧めしないと書いてあります。

However, it should generally be avoided as a setter in production-ready code, because it’s generally best to keep presentational information out of JavaScript code.  

適当訳:「しかし、製品用のコードでsetterを使うのはふつう避けられるべきです。なぜなら一般的に、プレゼンテーション情報はJavaScriptコードの外側に書くのが良いからです。」

というわけで、setterでCSSを直接変更するのはやめて、あらかじめCSSに変更したいプロパティを書いておいて、classを変更することで対処せよと書いてあります。明日この機能を使うつもりだったので、読んでおいて正解でした。下の例なら .big にfont-size: 100px; などを設定しておけばいいですね。

Instead, write CSS rules for classes that describe the various visual states, and then change the class on the element.
 
// Working with classes.
 
var h1 = $( "h1" );
 
h1.addClass( "big" );
h1.removeClass( "big" );
h1.toggleClass( "big" );
 
if ( h1.hasClass( "big" ) ) {
...
}

 

 


jQuery: jQueryオブジェクトの変数名には$を付けるという慣習

learn.jquery.com

jQueryオブジェクトはラッパーです。セレクタで要素を選択するごとに、毎回新規作成されます。商品がレジ袋で包まれるようなものです。

var logo1 = $( "#logo" );
var logo2 = $( "#logo" );
alert( $( "#logo" ) === $( "#logo" ) ); // alerts "false"

全く同じ要素をselectしているのに、別々の袋に入れられているため === では別物として扱われるわけです。(C言語だったらポインタの比較で説明できそう)

しかし .get(0) で中身を取り出して比較すれば、これは同じものを参照しているので同一となります。

var logo1 = $( "#logo" );
var logo1Elem = logo1.get( 0 );
var logo2 = $( "#logo" );
var logo2Elem = logo2.get( 0 );
alert( logo1Elem === logo2Elem ); // alerts "true"

公式サイトでは、紛らわしいのでjQueryオブジェクトの変数名には$を付けようぜ!と言っています。

var $logo1 = $( "#logo" );
var logo1 = $logo1.get( 0 );
var $logo2 = $( "#logo" );
var logo2 = $logo2.get( 0 );
alert( logo1 === logo2 ); // alerts "true"

jQueryオブジェクトは $logo1、生のDOM要素は logo1 と書き分けることで、コードの見栄えが良くなりました。私も今後これに従おうと思います。


詰碁印刷3 碁盤と碁石と手順の描画、javascriptで配列の簡単なシャローコピーの作り方

詰碁印刷2 – 六帖のかたすみ
の続きです。

ソースはこちら
kickzone/SGFPrint · GitHub

f:id:happyholiday:20150628111339p:plain

碁盤と手順を表示できるようになりました。使っている技術は大したことはなく、easeljsでゴリゴリ碁盤や碁石を描いているだけです。
一番苦労したのはノードの全分岐をゲットする処理です。これは先週大まかにできていましたが動作はさせておらず、今週デバッグに時間がかかりました。
詰碁用のSGFファイルは正解図を表示させるだけではありません。この手ならここがだめ、じゃあ少し戻ってここはこう打つ…と分岐がいくつも存在するファイルがありますので、すべてのパターンについて出力が必要となります。スタックと配列のコピーで実装できました。

SGFElement.prototype = {
//全てのノード組み合わせ(=すべての分岐)をゲット
getAllBranch: function(){
var retArr = [];
var nodeStack = [];
var terminals = [];
nodeStack.push(this.root);
while(nodeStack.length){
var currentNode = nodeStack[nodeStack.length-1];
//子ノードがなければ終端とみなし、nodeStackの内容をretArrにコピー
if(currentNode.childNodes.length == 0){
terminals.push(currentNode);
retArr.push([].concat(nodeStack)); //配列をシャローコピーするちょっと変わった書き方
nodeStack.pop();
}
else{
//terminalsに入っていない子ノードをスタックに入れる
//一つもなければ、探索が終了したものとして終端一覧に入れ、自分自身をpop
var findChild = false;
for(i in currentNode.childNodes){
var node = currentNode.childNodes[i];
if(terminals.indexOf(node) == -1){
findChild = true;
nodeStack.push(node);
break;
}
}
if(!findChild){
terminals.push(currentNode);
nodeStack.pop();
}
}
}
return retArr;
}
}

配列のシャローコピーはとても簡単な方法があります。

var newArr = [].concat(oldArr);

と書くだけでよいです。空の配列に古い配列をコピーした結果がnewArrに返ってきます。

残りの作業は、SGFファイルに書かれているコメントの出力と、大詰めのCSSによるページ設定です。これができれば一通り完了です。


PHP: GETとPOSTの思想的違い

フォームなどからサーバーにデータを渡す時に使用する GET と POST は、主に形式的な違いのみが取り上げられがちです。私もそのように理解していました。しかしProgramming PHPにはそのように書いてありませんでした。

形式的な違い

GETはURLの後に?をつけて

http://rokujo.esy.es/StockHoloscope/chart.php?mcode=4753&start=20050601&end=20060531

のようにサーバーにパラメーターを渡してやる仕組みです(リンク先はライブドア爆下げの様子です)。データ量が多くなればURLも長くなるので、当然ながら転送量にも限りがあります。また、URLからパラメータが丸見えなのでデータの安全性が低いです。

POSTはURLには手を加えないで、サーバーに直接データを渡してやります。テキストだろうがバイナリだろうがOK、データ量の制限もありません。プログラマからするとPOSTの方がデータが取り出しにくくちょいと面倒です。

思想的な違い

GETはidempotentでないといけません。初めて聞く単語ですね。これは数学用語で、日本語では冪等(べきとう)と言います。定義は

「何回操作を行っても、得られる結果が同じ」

です。つまり、GETは何回行っても必ず同じ表示がされなければいけません。必ず同じ表示がされるならば、URLをブックマークすることも可能です。

これに対して、POSTはidempotentである必要はありません。POSTすることによってウェブページが変化しても問題ありません。むしろ変化することが当たり前です。POSTを行ったページを再表示すると「フォーム再送信の確認」とか「webページの期限切れ」などと表示されます。これはページがPOSTの度に変化してしまうということですから、POSTに冪等性が無いことを表します。ブックマークも不可能です。

例えば

http://this.is.bbs/bbs.php?author=gokuu&content=icchoyattemikka

(URLは架空です)と、形式的にGETで掲示板への書き込みのようなidempotentでない動作をさせることは可能ですが、思想的に許されていません。URLをクリックする度にデータを変化させるようなアクションがあってはならないのです。GETはGETの名前通り、データの取得に徹しなくてはなりません。


jQuery, エレメント作成時には予約語のclassに注意

jQueryはDOMエレメントを動的生成できます。私の探していた機能のうちの一つです。ところが、次のコードを見ると不自然なダブルクォーテーションが見つかります。

// Creating a new element with an attribute object.
$( "<a/>", {
   html: "This is a <strong>new</strong> link",
   "class": "new",
   href: "foo.html"
});

すごく直観的で分かりやすいコードなのに、”class”、が囲まれているのが不自然ですね。これは、classがjavascriptの予約語なのでそのまま class と書くことができないから、なのだそうです。

予約語一覧を見ると、他にDOM作成時に使われる可能性がある予約語はdefault, forくらいですが、あまり使う頻度は高くなさそうです。classはしょっちゅう使いますから、これだけ覚えておけば当面は問題なさそうですね。