関数(メソッド)の再定義と多重定義
昨日ごりごりRuby書いているときに他の言語の感覚のままでハマったこと - 日々の御伽噺を読んで、C++ではどうかと思い確認した。
int foo(int a, int b) { return 1; } int foo(int a, int b, int c = 0) { return 0; } int main() { return foo(1, 2); }
% g++ -Wall -Wextra -O0 -g func_overload.cc func_overload.cc:1: warning: unused parameter ‘a’ func_overload.cc:1: warning: unused parameter ‘b’ func_overload.cc:6: warning: unused parameter ‘a’ func_overload.cc:6: warning: unused parameter ‘b’ func_overload.cc:6: warning: unused parameter ‘c’ func_overload.cc: In function ‘int main()’: func_overload.cc:13: error: call of overloaded ‘foo(int, int)’ is ambiguous func_overload.cc:1: note: candidates are: int foo(int, int) func_overload.cc:6: note: int foo(int, int, int)
ということで、曖昧な呼び出しとしてコンパイルエラーになった。ただし、呼び出し側の引数を3つにするか、上記二つ目のfoo()で引数からデフォルト値を取り除けばコンパイルは通る(それでいいのかどうかは状況による)。
同じ関数(メソッド)名を再度定義した場合、Rubyでは再定義(オーバーライド)、C#では多重定義(オーバーロード)となるようだ。C++では関数の再定義は行えないが、上記のように多重定義は行える。ただし呼び出し方によって曖昧なものとして扱われコンパイルに失敗する、ということか。
Google C++ Testing FrameworkをVisual Studio Express 2012 for Windows Desktopでビルドした
ちょっと必要になったのでGoogle C++ Testing Framework(以下gtest)をVisual Studio Express 2012 for Windows Desktop(以下msvc11)でビルドしました。といっても、やったのはソリューションファイルとプロジェクトファイルを用意しただけ。差分はgistにあげました。
https://gist.github.com/iwadon/4722737 *1
gtest-1.6.0を展開したディレクトリからpatchするとmsvc11ディレクトリが出来上がるので、その中のソリューションファイルを使ってビルドします。
*1:当初はgistを貼り付けてましたが、縦にやたら長くなってしまったのでリンクのみにしました。
C#でMessagePackを使う(その後)
C#でMessagePackを使う
C++で書いたアプリとC#で書いたアプリの間でMessagePackを使ってデータをやりとりできないかな、とやってます。C++の方はなんとか使い方を覚えましたが、C#の方はC#自体ここ一ヶ月くらいしか経験がないせいもあって苦労してます。どこかに質問しようにも頭の中を整理しないと始まらないので、ここにメモします。
参考
- Format specification - MessagePack - Confluence ... シリアライズ結果のフォーマットが説明されています。
- 主に技術日記: MessagePack for CLI ... C#版MessagePackの作者による紹介です。
C++でMessagePackを使う
まずは勝手知ったるC++から。ドキュメントやソースコードを読みながら使い方を覚えました。
#include <string> #include <msgpack.hpp> struct Foo { std::string str; MSGPACK_DEFINE(str); }; int main() { Foo foo; foo.str = "Hello"; msgpack::sbuffer sbuf; msgpack::pack(sbuf, foo); // 91 a5 48 65 6c 6c 6f と変換される return 0; }
ユーザ定義クラスはMSGPACK_DEFINEマクロで指定した変数が並んだ配列としてシリアライズされます。Rubyでいうところの["Hello"]
と同等の状態ですね。MSGPACK_DEFINEマクロでpackとunpack両方の仕組みが自動的に定義されて便利です。
C#でMessagePackを使う(1)
最初はVisual Studio 2012のNuGet経由でインストールしたところ、これは二代目の実装だそうです。バージョンは0.1.0.2011042300。以下は説明のためのコードです。
using MsgPack; // 中略 class Foo { string str; } // 中略 Foo foo = new Foo { str = "Hello }; var packer = new CompiledPacker(true); byte[] msg = packer.Pack<Foo>(foo); // 81 a3 73 74 72 a5 48 65 6c 6c 6f と変換される
ユーザ定義クラスのオブジェクトはmapとしてシリアライズされています。これはC++でのシリアライズ結果と異なるため、両者に互換性がないことになります。
C#でMessagePackを使う(2)
次に三代目の実装(MessagePack for CLI)を使います。バージョンは0.3-beta2。こちらはデフォルトで配列としてシリアライズされるそうなのでC++での結果と互換性がありそうです。
using System.IO; using Msgpack.Serialization; // 中略 // (Fooクラスは上記を参照) MemoryStream stream = new MemoryStream; var serializer = MessagePackSerializer.Create<Foo>(); // ここで例外 System.TypeLoadException が発生する serializer.Pack(stream, foo); stream.Position = 0; byte[] msg = new byte[256]; stream.Read(buf, 0, (int)stream.Length);
一応上記のように実装はしてみましたが、例外System.TypeLoadExceptionが発生してしまいます。
というところで今日は時間切れです。
ケアレスミス
どうしても直せない不具合があった。ifの条件が真ではないことが明らかなのに真とみなされてしまう。かなり頭を抱えたが、よくよくソースを見たらなんてことはない(以下は適当な再現コード)。
bool ret = true; if (!ret); // ← ここに謎のセミコロンがある { }
なんだよこのセミコロンw if文の意味ねー…orz 結局数時間悩んだ問題はセミコロンを一つ削除することで対処できた。
ちなみにこれ、別ラインの同じソースでも同じ原因で同じように悩んで同じように修正してるはずなんだよね。その場でこちらのソースも修正すれば良かった。過去の俺もっと頑張れよ(無茶振り)。