2014年5月18日日曜日

ダブルディスパッチの対象をテンプレートクラスにしたら動かなくなった

以下はダブルディスパッチの例
#include <iostream>

struct Sub;

struct Super
{
    virtual void Func(Super&) = 0;
    virtual void Func(Sub&) = 0;
};

struct Sub : public Super
{
    void Func(Super& a)
    {
        std::cout << "Sub::Func(Super&)" << std::endl;
        a.Func(*this);
    }
    void Func(Sub& a)
    {
        std::cout << "Sub::Func(Sub&)" << std::endl;
    }
};

int main()
{
    Super* a = new Sub();
    Super* b = new Sub();
    a->Func(*b);
    return 0;
}
出力結果は以下のとおり

Sub::Func(Super&)
Sub::Func(Sub&)

Sub は自分自身の型を知っているので、オーバーロードされた適切な関数を呼ぶことが出来る。 勿論、オーバーロードされた関数でなくとも良い。

ところが、以下のコードはエラーとなる。
#include <iostream>

template<typename T>
struct Sub;

struct Super
{
    virtual void Func(Super&) = 0;
    template<typename T>
    virtual void Func(Sub<T>&) = 0;
};

template<typename T>
struct Sub : public Super
{
    void Func(Super& a)
    {
        std::cout << "Sub::Func(Super&)" << std::endl;
        a.Func(*this);
    }
    void Func(Sub& a)
    {
        std::cout << "Sub::Func(Sub&)" << std::endl;
    }
};

int main()
{
    Super* a = new Sub<int>();
    Super* b = new Sub<int>();
    a->Func(*b);
    return 0;
}
テンプレート関数は仮想化することが出来ない。 何故かというと、テンプレート関数は呼び出しを見つけた際に、型に応じた関数を生成するので、 仮想関数テーブルがクラスの定義から一意に決定出来なくなってしまうからだ。

スーパークラス側でテンプレートを決定してしまえば問題は解決するが、そうすると様々な型同士で処理可能にするという目的を達成できない。 さて、どうしたものか……

2014年4月30日水曜日

std::vectorとコピーコンストラクタと仮想関数

#include <iostream>

struct Value
{
	int v;
	Value()
	: v(32)
	{}
	~Value()
	{
		v = 0;
	}
	void Func()
	{
		std::cout << v << std::endl;
	}
};

int main()
{
	Value* p;
	{
		Value a;
		p = &a;
		p->Func();
	}
	p->Func();
	return 0;
}
最初のFuncの呼び出し時は、aは解体されていないので32が出力される。
二回目のFuncでは、aは解体済みなので、エラーになるか、または0が出力される。
#include <iostream>

struct ISample
{
	virtual void Func() = 0;
};

struct Sample : public ISample
{
	void Func()
	{
		std::cout << "Sample::Func" << std::endl;
	}
};

int main()
{
	ISample* p;
	{
		Sample s;
		p = &s;
	}
	p->Func();
	return 0;
}
main関数内のブロックを抜けた時点で、sのデストラクタが呼ばれ、解体される。
Funcを呼び出した時点で、仮想関数テーブル内のデータが失われていた場合、
「純粋仮想関数がコールされた」という、初学者にとっては見慣れないエラーとなって現出する。
#include <iostream>
#include <vector>

struct ISample
{
	virtual void Func() = 0;
};

struct Sample : public ISample
{
	void Func()
	{
		std::cout << "Sample::Func" << std::endl;
	}
};

struct Object
{
	ISample* p;
	Sample s;
	Object()
	: p(&s)
	{}
};

int main()
{
	Object o;
	o.p->Func();
	
	std::vector<Object> ov;
	ov.push_back(Object());
	ov[0].p->Func();
	
	return 0;
}
最初のFuncの呼び出しは問題ない。
二回目、vectorへの要素の追加が問題。
push_back関数の実引数の部分で、まず無名ローカル変数としてObjectのインスタンスが生成される。
そして、vectorが確保している領域へ、コピーコンストラクタによってコピーされる。
ここでコピーコンストラクタが用いられることが問題。
私は、てっきり自分で定義したコンストラクタでvector内の領域が初期化されると思っていたが、
実際にはコピーコンストラクタが呼ばれるので、コピーされたpが指すアドレスは、
コピーされた先のアドレスではなく、元となった無名ローカル変数内のsのアドレスとなる。
このオブジェクトを後で使おうとすると、エラーの原因となる。
もしエラーが出ても、「純粋仮想関数がコールされた」という初学者にとっては一見意味不明なエラーとなる。
このエラーを特定するのに3日近くかかってしまった。

2014年4月29日火曜日

Surface Pro 2を買って失敗したな、と思った話

先日Surface Pro 2を買った。
非常によくできていて、素晴らしい製品なのだが、早くも後悔している。
出来が悪かった訳では無い。
自分のニーズを見誤っていたのだ。

自分は以下の様なニーズでSurface Pro 2を購入した。

  • Windowsを搭載し、コーディングが出来るモバイル環境。
  • 展示等で利用可能な、長時間バッテリー駆動可能なタブレット。
  • ごろ寝で利用出来るタブレット。
実際には、Surface Pro 2はコーディングは出来るし、性能も十分なものの、モバイル環境とは言え、コーディングするには画面が小さく使いづらかった。10.6インチのフルHDディスプレイは等倍では文字が小さくなりすぎて見辛く、かといって文字を拡大するとWindowsのアプリケーションはきちんと対応したアプリケーション以外は多かれ少なかれ表示が崩れてしまう。

展示等での利用には確かに向いているのかも知れないが、数年に一度使うか使わないかなので、そのためにSurface Pro 2を購入する、というのは愚策だった。普通のラップトップを流用しても全然構わなかったはずだ。これに関しては完全に自分の需要を見誤った。

そして、ごろ寝での利用について。Surface Pro 2はごろ寝でも利用出来ないことはないが、Core iタブレットなのでそこそこ重い。また、最も致命的なのが、ブラウジングし辛いことだ。

Windows 8.1で、Modern UIでブラウジングする場合、Internet Explorer以外にマトモな選択肢は無いのだが、このModern UI版Internet ExplorerはAAの表示が崩れるのである。これでは使い物にならない。また、Modern UI版Internet ExplorerはInternet Explorerが規定のブラウザでないと使用出来ない。

そのため、AAが崩れないようにGoogle Chromeを導入したのはいいのだが、Chromeはタッチで全操作を行うにはUIが小さく使いづらい。かといって、画面の拡大率を125%にしてもChromeの表示は拡大されず、150%にして初めて拡大される。
しかしながら、それでもUIは小さく使いづらい上、Chromeでは画面が滲んで表示されてしまう。おそらく、表示崩れを起こさないために、100%のサイズでレンダリングした結果を、150%に拡大表示しているのだろう。

Windowsにおける、UIを拡大するための機能は、どうしてもUIの崩れを引き起こしてしまう。これは、過去のソフトウェア資産を活かすため、互換性を高く保つための設計になっていることが大きな要因の一つだろう。iOSや、Mac OSが、高精細な画面を搭載するにあたって、非対応アプリケーションを丁度倍角でニアレストネイバー法で拡大表示したのが、如何に英断だったか、というのを感じさせられた。これから、スマホ向けの高精細ディスプレイが、より大きなディスプレイに波及していく流れにあたって、どのような大きさでも表示崩れを起こさずに綺麗にレンダリング出来ることは重要になっていくだろう。

以上の様な理由で、Surface Pro 2の購入は失敗だった、と思っている。最初から、普通のラップトップを買えばよかったのだ。

2014年3月17日月曜日

Repliseフォント公開しました。

NYSL ライセンスにちょっとした禁止事項を加えた形で公開してます。

repliser-font - free font - Google Project Hosting
https://code.google.com/p/repliser-font/


加えてる禁止事項に深い意味はなくて、1つ目は余計なトラブルを防ぐため、2つ目は以下の様な事例を防ぐためです。

「亡き母を想って作ったゲームが奪われようとしている」CandySwipe作者から、Candy Crush Sagaへの手紙(和訳) - どらろぐ
http://drahtpuppe.tumblr.com/post/76590165464/candyswipe-candy-crush


基本的に Windows 環境でのみテストしてるので、他の環境だと正常に表示できないかもしれません。 特に、行間のスペースとかは全く正常に動作しない可能性が高いです。

利用の際に許諾を得る必要はありません。 また、利用の際に私の名前は書いても書かなくてもOKです。 ただ、ご一報あれば喜びます。私が。

丁度 git が流行ってるみたいなので、 OSS としてフォントを公開するならちょうどいい機会かと思って Google Code を利用しました。 Replise フォントをベースにしたフォントを自由に制作、公開できます。 GitHub と Google Code のどちらを使うか迷いましたが、とりあえず Google Code を使うことにしました。 深い理由はなくて、 textar-font が Google Code を利用していたからです。

Textar Font - アスキーアート対応ウェブフォント
http://font.textar.tv/


textar-font - A japanese propotional font for Shift_JIS art. - Google Project Hosting
https://code.google.com/p/textar-font/


また、 ttf ファイルの直接ダウンロードは Google Drive を利用しています。理由は以下の記事を参照。

ダウンロードファイルはGoogle Driveへの移行を推奨:Google Code、ダウンロード機能の提供を中止 - @IT
http://www.atmarkit.co.jp/ait/articles/1305/23/news095.html


アクセス解析には Google URL Shortener を使っています。 なので出来れば展開した短縮URLを直接張ったりはしないで欲しいなーなんて思ってます。

Google URL Shortener
http://goo.gl/


制作には fontforge を利用しました。

FontForge
http://fontforge.org/ja/

Copyright © 2000,2001,2002,2003,2004,2005,2006 by George Williams

現在はプロポーショナルフォントのみの公開で、モノスペースフォントはまだ作ってません。

後々、気が向いたらフォント制作の際に得たノウハウや、今回gitを導入した時のエントリとかかくと思います。

いじょ。

2014年3月1日土曜日

Lenovo ThinkPad USB トラックポイントキーボード 55Y9024 を分解した

ThinkPad USB トラックポイントキーボードの調子が悪くなった、具体的には'3'キーがうまく動かなくなったので、分解掃除を行った。

結論から言うと、爪が挟まっていた。そして分解する必要は無かった。

特定のキーが効かなくなった時の掃除ならば、全体を分解する必要はなく、キートップを外すだけでよかったのである。

ちなみに、 ThinkPad USB トラックポイントキーボード 55Y9024 のキートップは4箇所をパチッと止めてあるだけなので、キーの隙間から爪を引っ掛けてちょっと力を加えれば簡単に外す事が出来る。当然ながら壊れたとか言われても責任はとりません、あしからず。

分解には以下のエントリを参考にした。

  • ネジ穴は見えてる6本+シール裏両サイドの2本の計8本
  • シールは破れやすい。破れるとたぶん保証が効かなくなる。ドライヤ等で温めて剥がすべし。ちなみに自分は破れた。
  • Shiftのあたりのツメが曲者。詳細は後述。
  • キーボードに繋がっている配線が外れやすい。開けたら向きを確認すること。
  • Shiftキー付近以外のツメは、反対側に引っ張るようにして外すとよい。
  • 上端は外さなくてもキーボード本体を外せる。無理して外さない方が良い。
で、リンク先のエントリにも書いてあるShiftキー付近のツメについて。
画像中心の凹みがツメを引っ掛けるための部分。かなり固めなので無理にこじ開けようとするとバキっと割れかねない。裏側からキーボードに対して垂直にマイナスドライバを挿し込んで、水平にスライドさせつつ開けるとうまくいくと思います。繰り返しますが、壊れても責任は取りません。

サンワサプライ SlimPort-HDMI変換アダプタ AD-HD14SP レビュー

Kindle コミックを大量に買った。大画面で快適に楽しみたい。かといってタブレットを買うのは億劫だ。仮に買うにしても Windows タブレットがいい。 Windows 版 Kindle は米 Amazon で購入した本しか読めない。そうだ、スマートフォンから PC 用ディスプレイに出力してやればいい。

というわけで サンワサプライ SlimPort-HDMI変換アダプタ AD-HD14SP を買った。



このアダプタ、初期不良報告が多い。というか、SlimPort-HDMI変換アダプタは全て「映らない」「一瞬だけ映った」等のレビューが散見される。返品・交換の手順がハッキリと書かれている Amazon で購入するのが心理的に良いだろう。

幸い自分の元に届いたアダプタは一応動作したが、不安定だ。映る時はきちんと映るのだが、うまく映らずに四苦八苦する事が少なからずある。大体以下にリストアップした事を試すとうまく動く。
  • ディスプレイに接続されている他のケーブルを全て抜く。
  • アダプタに接続されたUSBケーブルを抜く。
  • USBケーブルをPCでは無くACアダプタに接続する。
  • ディスプレイの電源を付け直す。
  • スマートフォンとアダプタを接続し直す。
以上である。その他色々やって、どうしても動かないこともあるかもね。

2014年2月22日土曜日

LuaBinariesのマニュアルの一部翻訳

lua5.1.dll と lua51.dll の違いが分からないので調べていたら LuaBinariesの公式マニュアル に行き着いた。

どうやら、実行自体には lua5.1.dll と lua51.dll の両方が必要で、プロジェクトに追加する依存ファイルは lua5.1.lib で問題ないっぽい。

WindowsのCランタイムライブラリとその制限事項、及びLuaBinariesのビルド環境等の根深い要因が関わってくるらしく、よく理解できませんでした。

きちんと訳せている自信が全くない、誤訳だらけな気がするので、ちゃんとした情報を知りたい方は翻訳元を自分で読んで下さい。もし間違ってたらご指摘お願いします。

[ 2014.02.23 追記 ]
スタブライブラリは .lib ファイルらしいです。
What's the purpose of the lua "stub" dll for windows - Stack Overflow
プロジェクトとスタブライブラリを関連付けて初めてdllの中身を呼べるようになる訳ですな。必要なdllはlua5.1.dllのみっぽいです。後日検証します。

イマイチよく分かりませんが、lua5.1.dllがluaのコアdll、lua51.dllはLua Binariesのdllで、それぞれを使うにはそれぞれ、libファイルと実行ファイルをプロジェクトの設定でリンクする必要がある、基本的にはlua5.1.dllとlua5.1.libがあればいい、ということでしょうか。lua51.dllとlua51.libの使い方がまるで分からんぞ!
[ 追記ここまで ]


以下引用と翻訳。

LuaBinaries for Windows Compatibility

LuaBinaries for Windowsの互換性

In Windows your library or application must be linked with a stub library. A stub library is a library with only the function declarations that will bind your DLL with the Lua DLL. The LuaBinaries DLL is called "lua52.dll".

Windowsにおいて、あなたのライブラリまたはアプリケーションはスタブライブラリとリンクされなければならない。スタブラリブラリはあなたのDLLをLua DLLと結びつける関数定義のみのライブラリである。そのLuaBinariesのDLLは「lua52.dll」と呼ばれる。

For Lua 5.1

Lua 5.1について

We choose the Visual C++ 8 compiler to build the LuaBinaries executables.

我々はLuaBinariesのプログラムをビルドするのに Visual C++ 8 コンパイラを選択した。

Up to Visual C++ version 6, the C run time library was called "msvcrt.dll" and this DLL is today included in all modern Windows systems (W2K, WinXP, Vista and 7). But for Visual C++ 8, Microsoft released a free version of the compiler called "Express". We consider that an important step for developers using LuaBinaries DLLs. Also It is know that "msvcrt.dll" has some problems and limitations, particularly with multi-threading. So we decided to move to Visual C++ 8 version for which the C run time library is called "msvcr80.dll".

Visual C++ version 6で、Cのランタイムライブラリは「msvcrt.dll」と呼ばれた。そして、このDLLは今日全てのモダンなWindowsシステム ( W2k、WinXP、Vista、そして7 ) に含まれています。しかし、Visual C++ 8においては、マイクロソフトは「Express」と呼ばれるフリーバージョンのコンパイラをリリースした。我々はLuaBinariesのDLLを使う開発者にとって重要なステップを熟考した。また、それは「msvcrt.dll」はいくつかの問題と制限、特にマルチスレッドにおける―を抱えていることが知られている。そのため、我々は「msvcr80.dll」と呼ばれるCランタイムライブラリのためにVisual C++ 8バージョンへの移行を決定した。

The LuaBinaries DLL packages have a dll proxy called "lua51.dll". It can be used to replace other "lua51.dll" released by other distributions. It will simply forward calls to the "lua5.1.dll". There is no compiled source code involved in the forwarding.

LuaBinariesのDLLパッケージには「lua51.dll」と呼ばれるdll proxyが含まれます。それは、他のディストリビューションによってリリースされた、他の「lua51.dll」を置き換えることが出来ます。それは単純に「lua5.1.dll」に関数コールを転送します。forwardingに含まれるコンパイルされたソースコードはありません。