Visual Studio2012で「Uniform initialization and initializer_lists」を試す

VC++2012のNovember CTP of the C++ compilerお試しその4。
initializer_listを試しましょう。
ですが、コンパイラが対応しただけで、STLの更新はまだみたいなのでvectorとかで使ってみるというわけにはいきません。

一番簡単なサンプル

とりあえず、初めてなのでこんな感じで。

#include <iostream>

int main()
{
	for(auto i : { 2, 3, 5, 7, 9, 13 ,17, 19 }) {
		std::cout << i << std::endl;
	}
	return 0;
}

Josuttisさんの「The C++ STANDARD LIBRARY second edition」にあったコードをベースにしています。引用するほどの大げさなコードではないですけれども。
インクルードするヘッダはだけでいいのかしら?
一応GCC(4.7.2)では動きました。

さて同じコードをVC++2012 November CTPに食わせますと、

error C3312: no callable 'begin' function found for type 'initializer-list'
error C3312: no callable 'end' function found for type 'initializer-list'
error C2065: 'i' : undeclared identifier
IntelliSense: 式が必要です

と言われます。
なんだかよくわからないので

#include <initializer_list>

を追加すると…

error C2668: 'std::begin' : ambiguous call to overloaded function
error C2668: 'std::end' : ambiguous call to overloaded function

となります。さっぱりわかりません。

auto initList = { 2, 3, 5, 7, 9, 13 ,17, 19 };
for(auto iter = initList.begin(); iter != initList.end(); ++iter) {
    std::cout << *iter << std::endl;
}

ならビルドが通って実行可能なのですが、これはいやだな。
このコードも「#include <initializer_list>」は必要のようです。
無いと

error C3086: cannot find 'std::initializer_list': you need to #include

と怒られます。
では、GCCで「#include <initializer_list>」しなくても動いたのは何故でしょう?
内でインクルードされるヘッダのなかにが含まれているということかしら。
内でインクルードされるヘッダのなかにが含まれるというのが、もし仕様的に正しくてもVCの今回の対応にSTLは含まれないので無理。
いちいち仕様を確認するのも面倒だし(コンテナ系のヘッダをインクルードすれば<initializer_list>もインクルードされるだろうけど)「#include <initializer_list>」は必要と考えたほうがよさそう。

コンストラクタと代入演算子

コンストラクタと代入演算子を試したいのですが、STLの対応はまだですので機能を確認するだけの手抜き簡易コンテナを作成します。

template <typename T>
class Vec
{
	std::vector<T> content_;	
	template <class X, class... XS>
	void calldisp_(X x, XS... xs)
	{
		std::cout << "call : ";
		print(x, xs...);
	}
public:
	typedef typename std::vector<T>::size_type size_type;
	typedef typename std::vector<T>::iterator iterator;
	typedef typename std::vector<T>::const_iterator const_iterator;

	Vec() { calldisp_("Vec()"); }
	Vec(std::initializer_list<T> list)
	{
		calldisp_("Vec(std::initializer_list<", typeid(T).name(), "> list)");
		for(auto iter = list.begin(); iter != list.end(); ++iter) {
			content_.push_back(*iter);
		}
	}
	Vec(const Vec& src)
	{
		calldisp_("Vec(const Vec& src)");
		for(auto item : src) {
			content_.push_back(item);
		}
	}
	Vec<T>& operator =(const Vec& src)
	{
		calldisp_("Vec<", typeid(T).name(), ">& operator =(const Vec& src)");
		content_.clear();
		for(auto item : src) {
			content_.push_back(item);
		}
		return *this;
	}
	Vec<T>& operator =(std::initializer_list<T> list)
	{
		calldisp_("Vec<", typeid(T).name(), ">& operator =(std::initializer_list<", typeid(T).name(), "> list)");
		content_.clear();
		for(auto iter = list.begin(); iter != list.end(); ++iter) {
			content_.push_back(*iter);
		}
		return *this;
	}
	T& operator[](size_type index) { return content_[index]; }
	const T& operator[](size_type index) const { return content_[index]; }
	size_type size() const { return content_.size(); }
	iterator begin() { return content_.begin(); }
	const_iterator begin() const { return content_.begin(); }
	iterator end() { return content_.end(); }
	const_iterator end() const { return content_.end(); }
};

とかやっておいて、実際呼び出してみます。
以下がテストコードです。

template <typename T>
void dispVec(const Vec<T>& vec)
{
	std::copy(vec.begin(), vec.end(), std::ostream_iterator<T>(std::cout, " "));
	std::cout << std::endl;
}

int main()
{
	// (1)
	Vec<int> vec1{1, 2, 3, 4};
	dispVec(vec1);
	// (2)
	Vec<int> vec2 = {100, 200, 300, 400};
	dispVec(vec2);
	// (3)
	Vec<double> vec3({1.1, 2.2, 3.3, 4.4}); // VC++2012 ではこれがNG。GCC4.7.2ではOK。何故?
	dispVec(vec3);
	// (4)
	Vec<int> vec4 = {};
	std::cout << "vec4 size = " << vec4.size() << std::endl;
	// (5)
	vec1 = {10, 20, 30, 40, 50};
	dispVec(vec1);
	// (6)
	vec4 = vec1;
	dispVec(vec4);
	
	return 0;
}

試すと、(3)の

Vec vec3({1.1, 2.2, 3.3, 4.4});

がコンパイルエラーになります。
原因はよくわかりません。GCC4.7.2では問題なくコンパイルできました。さて?
(4)の空リストを与えた場合はVec()が正しく呼び出されます。

initializer_listを引数に持つコンストラクタのない場合

次です。さらに手抜きコードを書きまして

struct IntPair
{
	int first;
	int second;
	// IntPair() : IntPair(0) {}
	IntPair() : first(0), second(0) {}
	// IntPair(int val) : IntPair(val, val) {}
	IntPair(int val) : first(val), second(val) {}
	// IntPair(int f, int s) : first(f), second(s) {}
	IntPair(int f, int s) : first(f), second(s) {}
};
std::ostream &operator<<(std::ostream &os, const IntPair &ip)
{
	os << "( " << ip.first << "," << ip.second << " )";
	return os;
}

以下でテストです。

// (a)
IntPair ip1{2, 3};
std::cout << "ip1 = " << ip1 << std::endl;
// (b)
IntPair ip2{999};
std::cout << "ip2 = " << ip2 << std::endl;
// (c)
IntPair ip3{};
std::cout << "ip3 = " << ip3 << std::endl;
// (d)
IntPair ip4 = {6, 7};
std::cout << "ip4 = " << ip4 << std::endl;
// (e)
IntPair ip5 = {111};
std::cout << "ip5 = " << ip5 << std::endl;
// (f)
IntPair ip6 = {};
std::cout << "ip6 = " << ip6 << std::endl;

これは問題なく動作しました。

おまけ1:試しにexlicit

最初サンプルコードのVec(std::initializer_list list)をexplicitにしてみましょう。

explicit Vec(std::initializer_list<T> list)
{
//(同じなので省略)
}

そうすると、テストコードの(2)がコンパイルできなくなります。。。
と思いましたが、(2)だけでなく(1)もエラーになりました。何故?
試しにGCCでコンパイルすると(1)はOK、(2)はエラーと予想通りです。
同様にIntPairのほうのコンストラクタを全部explicitにします。

explicit IntPair() : first(0), second(0) {}
explicit IntPair(int val) : first(val), second(val) {}
explicit IntPair(int f, int s) : first(f), second(s) {}

すると、(d)と(e)がエラーになる。
。。。はずでしたが、VCではすべてエラーになります。

おまけ2:せっかくなんちゃってコンテナがあるのでVariadic Template

せっかくテスト用になんちゃってコンテナがあるのでVariadic Templateで"emplace_back"を追加してみます。

template <class... Args> void emplace_back(Args&&... args)
{
	content_.push_back(T(args...));
}

を追加して試します。

Vec<IntPair> veci{};
veci.emplace_back(2, 3);
veci.emplace_back(4, 5);
veci.emplace_back(6);
dispVec(veci);

実行しますと

call : Vec()
( 2,3 ) ( 4,5 ) ( 6,6 )

ま、大丈夫、ということで。

意味の無いまとめ

STLも含めて対応が完了してから再度確認しましょう。
それまでは中途半端であることを認識して使うこと。

Visual C++2012でRaw string literalsをお試し

VC++2012のNovember CTP of the C++ compilerお試しその2。
Raw string literalsとやらを試しましょう。

#include "stdafx.h"
#include <iostream>
#include <locale>

int _tmain(int argc, _TCHAR* argv[])
{
	std::locale::global(std::locale("japanese"));

	const char* msg =R"(
こ
 う
  で
   す
    か
	   )";

	std::cout << msg << std::endl;

	const wchar_t* wmsg = LR"(
そ
 う
  み
   た
    い
	   )";

	std::wcout << wmsg << std::endl;
	return 0;
}

で、

と表示されます。

残念なことにIDE上は「R"(」の部分にエラーマークがでます。
実際には問題なくビルド可能です。

raw-string:
  " d-char-sequenceopt( r-char-sequenceopt) d-char-sequenceopt"
d-char-sequence:
  d-char
  d-char-sequence d-char
d-char:
  any member of the basic source character set except:
   space, the left parenthesis (, the right parenthesis ), the backslash \,
and the control characters representing horizontal tab,
vertical tab, form feed, and newline.

とあり、

std::cout << R"#(test."(test.)"test.)#" << std::endl;

てなことをやると

test."(test.)"test.

と表示される、と。
でも、区切り文字を$にすると

std::cout << R"$(test."(test.)"test.)$" << std::endl;

がビルドエラーです。なぜでしょうか?
→ よく見たら”basic source character”となっていました。「$」はね〜よ。

The basic source character set consists of 96 characters: the space character, the control characters representing horizontal tab, vertical tab, form feed, and new-line, plus the following 91 graphical characters:14
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’

Treat TFS as an Enterprise Symbol Server

以前投票した
Treat TFS as an Enterprise Symbol Server
のステータスが「Under Review」になったというメールが来た。

ステータスが変わるとメールで通知がくることを初めて知った。

これは単純にSymbol Serverとして動いてくれると便利だなと思い投票した。

.NETならまだしも、ネイティブアプリだとシンボルがないとデバッグができない。
シンボルがあれば特定の環境でしか発生しない障害のときダンプファイルを取得して解析することが可能になるのでシンボルの管理は大切になる。

今は、ビルドバッチで自動化するようなことはしてなくて、ビルド後に必要なときに手動でローカルのシンボルサーバに登録している。

なので、将来TFSを導入したときにシンボルの管理とシンボルサーバとして公開することを担ってくれると助かるんじゃないかな〜と。

八巻さんのセッション資料

3月の.NETラボの八巻さんのセッション資料はデブサミの時の資料をみればよかったのか。聞いたかもしれないけどすっかり忘れていた。
ということで改めて見せてもらっている。

懇親会で八巻さんのお話をうかがえたのもとても楽しかった。

20代のころはVBユーザだったのでグレープシティ(当時は文化オリエント)は憧れの会社だった。
2つほどコンポーネントも買った。
普通に大学を卒業していたら真っ先に就職先として考えただろう。
当然のごとくその頃の僕のヒーローはDaniel ApplemanとDon Boxだった。
Don Boxは別にVBの人ではないけど「COM大好き」だったので。COM大好きは実はいまでも変わってない。
結局、VB → COM → C++ ときて今に至るわけだ。
C++はよくわからなかったけど、そして今でもよくわからないけど(特にテンプレートは理解不能)、面白い本が沢山あり(特にということでは「C++オブジェクトモデル―内部メカニズムの詳細」「C++再考」「More Effective C++」、そして理解できなかったけど凄いということだけはわかる「Modern C++ Design」)、今では一番好きな言語になっている。なので「好きこそ物の上手なれ」とか全く当てはまらず。

とか思い出を書いている場合ではなく、Windows8 です。
というか、それ以前に今これを書いているVistaを7にしたい。
ノートPCなのでドライバやら付属ユーティリティなどが動かなくなったら困るので躊躇してたけどVistaではいろいろつらい状況になった。

.NETラボ2012年2月勉強会 資料へのリンク

今回はASP.NET、とくにMVCがメインな話題。
ASP.NETってなにそれおいしいの?」状態の僕としては勉強になる内容。
ということで例によってメモ。というか発表資料へのリンク。

  • ASP.NETを使ったWebシステム開発超入門講座

いつもながら基礎的なことから説明がある丁寧な発表。まあそのせいで時間がおすという面もあるわけだけど。
実際にWebFormとMVCHello WorldレベルコーディングがあったのはWeb素人の僕にはうれしい内容。
というか、自分で調査しろよって話だけれど。

  • 触ってみようTeam Foundation Service Preview

個人的にはかなり興味を持っているところなので(正確にはTeam Foundation Server に大変興味があるのだけれどクラウド版のTeam Foundation Serviceにももちろん興味あり)、面白く話を伺った。あとは自分で試してみるしかない。
発表終了後、早速招待コード要求を行ってみた。さていつ招待コードがくるのか?

  • ASP.NET MVC4 Betaの新機能紹介

[公開用]Netラボ2012年2月勉強会 asp.netmvc4 beta新機能の紹介
ASP.NET MVCをまったく知らない人にはポカンとしてしまう内容かも」とおっしゃっていたが全然わかりやすかった。新規テンプレートで個人的に面白いと思ったのは「Single Page Application」。後で試す。
それぞれデモがあったので非常にわかりやすい。デモの部分はあとでスライドで確認というわけにはいかないのでそこは勉強会に参加する大メリット。
キーワードだけでてきたasyncだけど、ASP.NETでのマルチスレッドの使いどころを懇親会のときにでも聞こうと思って忘れた。

  • patterns & practices "Project Silk" に見る HTML5 とモダンブラウザのためのWeb開発の今後

patterns & practices "Project Silk" に見る HTML5 とモダンブラウザのための Web 開発の今後
MSエバンジェリストの井上章さんの発表。
内容がすばらしいのはもちろんだけど、とにかく発表自体がさすがに上手で、勉強になったという以前に感動した。
内容が僕にとっては盛りだくさんだったので実際にサンプルをダウンロードしてコードと資料を勉強する必要がある。
単純にすごいと思ったのはJavaScriptBBQ Plug-in
jQuery Templateとかはなんか想像のできる範囲だけどこれはどうやっているのかまったくわからない。
また、クライアントでのキャッシングもどうやっているんだか想像つかず。デモで2回目はサーバーにリクエストが飛ばないことを確認したけど、どうやって実現するんだか。。これもサンプルコードで確認すること。

  • その他

木沢さんのLTはGladinetのお話。便利そう。あまりSkyDriveを活用してないけどこれは試してみる価値あり。
それにしてもSkyDriveで25G、Yahooボックスで50G (何とか会員になっているため)使えるってすごい容量だ。DropBoxは2Gなのに。
でも一番使っているのはDropBoxだったりして。
惜しかったのは、出版されてすぐ購入したASP.NET4がじゃんけん大会のプレゼントだったこと。参加してもよかったけどやはり持ってない人がもらうべきだと思うので念のため辞退。

デブサミ2012 2日目参加メモ

いろいろあってかなり遅いけど自分のためにメモ。
参加したのはこれ。選択するときに手抜きで単純に「開発プロセス」を選択。

MS寄りな僕的には現在TFSを調査中なわけだけど、やはりJenkinsの話も聞いてみたいということ、川口さんのお話ということで参加。一応自分はIT関連の仕事なんだけど自分の仕事はかなり肉体労働でIT化されていないな、と。
副作用は会場で売っていたオライリーのJenkinsの本を買ってしまったこと。
Developer summit continuous deliveryとjenkins

  • 10年後も通用する開発環境の秘訣 〜 継続的フィードバックとVisual Studio 11

メインイベント。やはり"バグ・ピンポン"には悩まされるので使ってみたいな〜。
ただこんなにそれぞれの立場で一気通貫で使えるような形にするのは大変なんだろうな、と思った。できればすごいけど。
あと、気になる点はお値段かな。

  • オフェンシブな開発〜「納品しない受託開発」にみるソフトウェア受託開発の未来

すごく面白かった。某大手SIer社内ベンチャー→MBOってすごい。
Devsumi2012 倉貫講演資料

Axure RPは要件定義とかで簡単に紙芝居を作れるツールということかしら。
どのくらい便利かは使ってみないとわかんない。→ たぶん使うことはない、ということか。

  • 攻めの運用の極意

運用って全く係わったことないので勉強になった(要復習)。
Devsumi2012 攻めの運用の極意

  • ライターズ・フィロソフィー―IT業界で書いて食っていくひとたちの哲学をきこう

みんなブログを書くようになって良い有用な内容が発信される状況で、IT系のライターとしては何をすればよいのか、みたいな話。
なるほど。そんなことを考えたこともなかった。

とってもとっても面白かった。とくに庄司嘉織さんが参入してから面白さが倍増した。
チームの決めごとが興味深い。

・テストを書く
・問題を根性で解決するな
・人を殺す以外なら何をやってもいい
・失敗を引きずるな

あとは「(バグのある)コードを憎んで人を憎まず」とか。これ、つい忘れてしまう。でも「自分の一週間前のコードだってクソである」と。まさにその通りだ。
三周まわったおれたちのアジャイル
レガシーの現実

呼び出し規約にやられた

OpenCVを既存のプロジェクトで使おうとしたらリンクエラー。
関数 cv::error が見つからないらしい。

確認したところ、このVC++プロジェクト、呼び出し規約が__stdcallになっている。
(コンパイラオプション /Gz が設定されている)

使っているOpenCVのビルドの呼び出し規約は__cdeclにしてある。

C:\Program Files\Microsoft Visual Studio 10.0\VC>undname ?error@cv@@YAXABVException@1@@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?error@cv@@YAXABVException@1@@Z"
is :- "void __cdecl cv::error(class cv::Exception const &)"

C:\Program Files\Microsoft Visual Studio 10.0\VC>

OpenCVのビルドし直しはイヤなので、特定のヘッダだけ呼び出し規約を変更できないかと思ったけどそういう芸が見つからず。

 #include <opencv2/core/core.hpp> // ここだけ__cdeclとしたいけど。

結局OpenCVを使うcppのみコンパイルオプションを変更して対応することに。
このcppで定義される外部へ公開される関数には明示的に呼び出し規約を設定しておく。。。


ついでにメモ(WIN32)*1

呼び出し規約 引数渡し順 スタック管理 名前修飾
__cdecl 右から左 呼び出し元がスタックから引数を削除 関数名の前にアンダースコアを付ける(_Foo)
__stdcall 右から左 呼び出された関数側がスタックから引数を削除 関数名の前にアンダースコアを付け、関数名の後ろに@と引数リスト内のバイト数を10進で明示(_Foo@12)
__fastcall 最初の2個のDWORDパラメータはECXとEDXレジスタ、残りは右から左 呼び出された関数側がスタックから引数を削除 関数名の前に@を付け、関数名の後ろに@と引数リスト内のバイト数を10進で明示(@Foo@12)
this 右から左。thisパラメータはECXレジスタ経由 呼び出し元がスタックから引数を削除 なし
naked 右から左 呼び出し元がスタックから引数を削除 なし

*1:「.NET&Windowsプログラマのためのデバッグテクニック徹底解説」より