meryngii.neta

今日も新たな"ネタ"を求めて。

std::result_ofの実装

std::result_ofを即席で作ってみました。

template <class> class result_of; // undefined

template <class Fn, class... ArgTypes>
class result_of<Fn(ArgTypes...)> {
public:
	typedef decltype((*(Fn*)0)((*(ArgTypes*)(0))...)) type;
};

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

#include <iostream>
#include <typeinfo>

template <int> struct A { };

struct Functor
{
	A<0> operator()();
	A<1> operator()(int);
	A<2> operator()(int, double);
};

int main()
{
	std::cout << typeid(result_of<Functor()>::type).name() << std::endl;
	std::cout << typeid(result_of<Functor(int)>::type).name() << std::endl;
	std::cout << typeid(result_of<Functor(int, double)>::type).name() << std::endl;
}
1AILi0EE
1AILi1EE
1AILi2EE

よく考えてみるとBoost.Functionなどのテンプレートパラメータの使い方と若干違うんですね…。

Committee Draft 1への日本からのコメントのその後

C++0xのCommittee Draft 1への日本からのコメントのその後について適当に書いてみました。
C++ CD1 Comment Status
まあ原文を読んでもらえば済む話なのですが、半年経った今改めて振り返るためにまとめました。
結構数があるので今回はtypoの修正等を省略させてもらい、独断と偏見によって技術系の提案のみに絞ります。

  • JP-5 raw-stringの結合規則
  • JP-8 decltype(T)にスコープ解決演算子::が使えない
  • JP-9 ラムダ式にムーブキャプチャ &&がほしい
    • ラムダ式に変数を取り込むキャプチャの方法としてコピーではなくムーブを使いたいという要望。
    • 名前の付いた変数からムーブすることはできないとしてrejected。
    • まともな解決策はあるのだろうか。
  • JP-10 ラムダ式の匿名関数オブジェクトの型にresult_typeがほしい
    • std::result_ofは指定された型のresult_typeを返すので、ラムダ式の型にもつけるべきだという主張。
    • 今のstd::result_ofはdecltypeで作られてるので関係ないとしてrejected。
  • JP-12 constexprの再帰を認めるべき
  • JP-14 inline namespaceの名前の重複について
    • inline namespaceとすると外側の名前空間でもアクセスできるようだが、外側の名前空間と名前が重複したらどうなるのかということについて。
    • openのまま。
    • using directive(using namespace 〜;)が自動的に挿入されたようになる(7.3.1.8)と書いてあるので、それで理由づけられるような気がする。
    • 812. Duplicate names in inline namespaces
  • JP-21 char16_t/char_32_tの対応が不十分
    • iostreamにあるwostreamにはu16ostreamをなど、文字列を扱うライブラリにUnicode版のtypedefを追加せよという要望。
    • 型が増加すると大変だから、面倒くさがらずに変換してくれということでrejected。
    • 現実的な対応ということなのだろうか。
  • JP-23 フリースタンディング環境で使用できるヘッダの追加
  • JP-27 標準ライブラリ関数にnoreturn属性を追加
    • exitなどはユーザ側に処理が返ってこないことが明確なのでnoreturn属性を付けてほしいという提案。
    • 見過ごされているような雰囲気。
    • extern "C"な関数に属性を付けられるのだろうか。
  • JP-28 例外クラスのUnicode対応
    • std::exceptionなどで例外メッセージにwchar_tやchar16/32_tを使いたいという要望。
    • 呼ぶ側が国際化せよということでrejected。
    • JP-21と同じ見方でよいのだろうか。
  • JP-30,31 nested_exceptionは木構造をサポートすべき、または削除すべき
    • 例外が連鎖するような場合のためにnested_exceptionというものが追加されたが、木構造をサポートしないと使い物にならないという話。
    • openのまま。
    • 関数の広がりは分岐しながら進んでいくので、自然に木構造になるということだろうか。
    • id:wraith13氏ががんばってくれたらしい。C++0x の nested_exception について 其の参(考察) - TrickDiary
  • JP-32 exception.what()のメッセージが役に立たない
    • 例外のメッセージを見ても何の例外かも分からないようなものばかりなので、役に立つものにするよう明記してくれという要望。
    • 実装依存、ということでrejected。
  • JP-38 bindの戻り値の型がムーブできない
    • bindの戻り値の関数オブジェクトは、ムーブが実装されておらずコピーされる可能性がある。
    • open状態だが解決案は提示されている。
    • USやDEからも同様のコメントあり。
  • JP-62 is_xxx関数がイテレータを返すのに違和感がある
    • 一般的にis_xxxはbool型を返すというルールがあるのに、is_sorted_until、is_heapというイテレータを返す関数がある。そこでこれらの名前はxxx_boundに変えるべきという提案。
    • 名前について合意が得られていないとしてrejected。
    • この名前になった元々の合意とやらはあるのだろうか…。
  • JP-63 discrete_distributionにinitializer_list版のコンストラクタがほしい
    • discrete_distributionという乱数のクラスでイテレータだけでなく初期化リストによっても初期化できるようにしてほしいという要望。
    • これはaccepted。最新のhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2836.pdf:N2836を参照とのこと。
    • 辞書によると離散型分布というものらしい。よく分からない。
  • JP-64 valarrayのinitializer_list版のメンバ
    • valarray& operator+= (initializer_list); が定義されていないことについて。
    • 見過ごされている予感。
    • ところでvalarrayって何に使うのだろう。
  • JP-74 basic_regexのinitializer_list版のメンバ
    • JP-64との関連で、basic_regx & operator= (initializer_list); が定義されていないことについて。
    • tentatively ready(とりあえず準備完了)状態。
  • JP-65,66 unspecified-bool-type からexplicit operator bool() constへ変更すべき
    • せっかくExplicit Conversion Operatorsがあるのだから使おうぜという話。
    • 見過ごされている予感。
  • JP-73 のUnicode対応
    • fstreamのファイル名はchar16/32_tどころかwchar_tも使えないということについて。
    • まだ議論されていないようだが、JP-21,28と同じ方針でrejectされそうな予感。
  • JP-75 Atomic Operation Libraryのenum/structの定義方法がCの古い文法になっている
    • typedef struct { 〜 } 〜;などの古い記法が使われているので直すべきという提案。
    • C言語との互換性を保つためにわざとこうしてあるとしてrejected。
  • JP-76 "Throws: Nothing"の記述ポリシーが一貫していない
    • Throws: Nothingが書かれていないものがあることについて。
    • 編集的な問題ではないとしてopenのまま。

コンセプトに関する提案(JP-20,26,29,33,39〜43,45,46,48〜50,52,53,55,60,67〜69,72,77,79,81)は、コンセプトが追放された模様なので扱いませんでした。復活したら書くかもしれません。

型消去と引数転送

右辺値参照 + 可変個引数テンプレートでBoost.Functionの真似事をしようと思ったのだが、なんだか面倒な問題にぶち当たってしまったらしい。

#include <utility>

template <typename... Args>
struct my_function
{
	template <typename T>
	struct object_invoker
	{
		static void invoke(void* ptr, Args&&... args)
		{
			(*static_cast<T*>(ptr))
				(std::forward<Args>(args)...);
		}
	};
	
	template <typename T>
	void operator = (T object)
	{
		this->ptr = new T(object);
		this->invoker = &object_invoker<T>::invoke;
	}
	
	template <typename... Args2>
	void invoke(Args2&&... args)
	{
		invoker(ptr, std::forward<Args2>(args)...);
	}
	
	void* ptr;
	void (*invoker)(void*, Args&&...);
};

struct A
{
	void operator() (int) { }
};

int main()
{
	my_function<int> f;
	f = A();
	
	int i1 = 0;
	f.invoke(i1);
	
	const int i2 = 0;
	f.invoke(i2); // エラー
}

様々な関数オブジェクトを使えるようにするために型消去*1と呼ばれているらしいテクニックを使っている。また、引数転送に右辺値参照*2を使っている。右辺値参照はあまりに絶妙なので試すたびに騙されたような気分になる。あと、もちろん可変個引数テンプレートは引数を複数個取るために使っている。
しかしこのコードは期待通りには動かない。よく考え直してみると、型消去のテクニックでinvokerはただの関数ポインタになるので、それ以降は関数テンプレートの引数推論が使えない。つまりそれ以降は引数転送ができないのだが、呼び出される関数はさらに先にあるので、不完全な転送になるのだと思われる。
object_invoker::invokeの引数はArgs2と等しくなるべきだが、実際にはそれはできない。上のコードでは仮に呼び出し先のArgsとしてある。
組み合わせているテクニック同士の相性が悪いのだろうか。それとも私の新機能の認識が根本的に間違っているのだろうか。一体Boostではどうやって実装するのだろう。

conceptが…

少し遅れたけれども、
C++0x 6 (2ch)
The Digitalmars-d-announce July 2009 Archive by thread
Conceptのお通夜会場はこちらです - にっき(pseudo)
C++0x Support in GCC

Concepts [no longer part of C++0x]

なんという…。
C++1xになるよりはいいか…。
ちなみにGCC 4.5ではExplicit conversion operators(explicit operator T()とかいうやつ)が使えるようになるらしい。

VMware上のUbuntuとフォルダ共有

これまでGCCを使うためにCygwinMinGWandLinuxなど様々と試したのだが、GCCをmakeするたびに必ずエラーが起きて、さすがにもう懲り懲りになった。かといって普段の作業はWindowsでやっているから、このためだけにLinuxに乗り換えるのも難しい。そんなときVMware Player上でUbuntuを試してみたら、これがなかなか使いやすいので、しばらくこれで実験してみようと思う。
インストールするだけなら非常に簡単だったが、Windowsとファイルを共有するには少し手間がいる。すでにいろいろなサイトで紹介されているが、自分用にメモを残すことにした。
ちなみに私の環境は次の通り。

まずUbuntu仮想マシンをダウンロードして解凍する。最初からインストールされた形で提供されているので、VMware Playerでそのまま使うことができる。
http://www.ubuntulinux.jp/products/JA-Localized/vmware
VMware Playerもダウンロードしてインストールする。なおダウンロードには無料の登録が必要のようだ。*1
http://www.vmware.com/jp/download/player/
解凍した仮想マシンの中のUbuntu.vmxをダブルクリックすれば仮想マシンが起動する。ファイルの共有をしないならこれで終わり。実に簡単だ。
Windowsとファイルを共有したい場合はもう少し手間がかかる。
まず「アプリケーション」→「アクセサリ」→「端末」で端末を開いて、諸々をアップグレードする。

sudo apt-get update
sudo apt-get -u dist-upgrade

dist-upgradeはあまり実行しない方がよいらしいが、普通にupgradeするだけだとlinux-header-genericなど必要そうなものがアップグレードできないようだ。
ファイルを共有するにはVMware Toolsをゲスト側にインストールする必要がある。またVMware Toolsは単独では配布されていないので、VMware Serverをダウンロードして取り出す。なお、Ubuntu仮想マシン版にそのまま付属しているopen-vm-toolsではファイルの共有ができない。
VMware Toolsを取り出すために、Linux用のTARファイルをダウンロードする。ダウンロードするには同じく無料の登録をする。
http://www.vmware.com/download/server/
TARファイル内のvmware-server-distrib/lib/isoimagesにあるlinux.isoを適当に解凍する。
次に仮想マシンを起動する。VMware Playerのメニューの「デバイス」→「CD/DVD(IDE)」→「ディスク イメージ ファイル(iso)に接続」をクリックして、先ほどのlinux.isoを指定する。するとデスクトップに「VMware Tools」が現れるのでこれを開く。この中のTARファイルをどこかにコピーする。~/srcなどを作ってそこに置けばいいかと思う。
さらにこれを解凍する。ファイルを右クリック→「ここに展開する」で展開する。
再び端末を開く。まずUbuntuにあらかじめ入っているopen-vm-toolsをアンインストールする。

sudo apt-get autoremove open-vm-tools

そしてVMware Toolsをインストールする。

sudo ~/src/vmware-tools-distrib/vmware-install.pl

インストールはEnterを押し続ければよい。インストールが終わったら仮想マシンを終了して、Ubuntu.vmxに次の内容を追加する。

sharedFolder.option = "alwaysEnabled"
sharedFolder0.enabled = "TRUE"
sharedFolder0.present = "TRUE"
sharedFolder0.writeAccess = "TRUE"
sharedFolder0.readAccess = "TRUE"
sharedFolder0.hostPath = "(共有フォルダのホスト上でのパス)"
sharedFolder0.guestName ="(共有フォルダのゲスト上での名前)"
sharedFolder0.eXpiration = "never"
sharedFolder.maxNum = "1"

あとは仮想マシンを起動してVMware Playerのメニューの「VMware Player」→「共有フォルダ」で「常に有効」を指定してOKを押すと、共有フォルダが有効になる。
ゲスト上では/mnt/hgfs/(guestName)にフォルダが現れる。これでようやく共有ができるようになった。

*1:vmware player download」とかで検索して別のサイトを当たればすり抜けることもできる。こんなでいいのだろうか。

GCC 4.4.0がリリース

http://gcc.gnu.org/gcc-4.4/
GCC 4.4.0がリリースされました。C++0xの機能の一部を取り込んでいます。これらを試すにはコンパイラに-std=c++0xというオプションを渡します。
http://gcc.gnu.org/gcc-4.4/cxx0x_status.html
右辺値参照、可変個引数テンプレート、初期化リストとこれらだけでもしばらく楽しめそうですね。コンセプトは現在別のbranchで開発中のようです。

volatileなオブジェクトのコピー(再び)

以前volatileとコピー・代入 - meryngii.netaということでvolatileにまつわるCとC++の非互換性について書いたのだが、C++仕様書の最後で丸々このことが触れられていることに気づいた。せっかくなので拙訳を用意した。

付録C.1.8 12項. 特別なメンバ関数
12.8 (クラスオブジェクトのコピー)
 
変更点: volatileなオブジェクトのコピー
 
暗黙に定義されたコピーコンストラクタと暗黙に定義されたコピー代入演算子はvolatileな左辺値を作ることができない。例えば、次の例はISO Cでは合法である:
 
struct X { int i; };
struct X x1, x2;
volatile struct X x3 = {0};
x1 = x3; // C++では不正
x2 = x3; // これもまたC++では不正
 
論拠: いくつかの対案が長い間討論された。引数をvolatile const X&に変えると、クラスオブジェクトについて高速なコードを生成するのが非常に難しくなるだろう。暗黙に定義された演算子ごとに2つの別々のシグネチャを用意することについての議論は、曖昧性を生んだり、ベースクラスとメンバによるこれらの演算子の形成について明らかにするルールを複雑にしたりすることについてうやむやな問題を引き起こした。
 
元の機能への影響: 正しい意味を持つ機能の削除。
 
変換の難しさ: 意味上の転送。もしコピーにvolatileの意味が要求されるなら、ユーザ定義のコンストラクタか代入が用意されなければならない。もしvolatileの意味が要求されないなら、明示的なconst_castが使える。
 
どのくらい広く使われるか: ほとんど使われない。

大して使われないのにデフォルトの演算子を増やして複雑にするわけにはいかなかったということのようだ。確かにこれ以上C++に暗黙の動作を増やしたらどうなるかを考えると納得がいく。