A mere hateblo

単なるはてブロです。

関数(メソッド)の再定義と多重定義

昨日ごりごり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を使う - A mere hateblo)の続き。

その後アレコレ調べたり試しましたが自分の理解や経験ではなんともならず、MessagePack互換のクラスを自作しました。実のところ、相手側のC++の方も配布されているソースコードをそのままコンパイル出来なかった*1ので、こちらもすでに書き下ろしてます。徐々にunpackするとか、一旦オブジェクトに入れてから好きな値だけ拾うとか、ゼロコピーとかはできませんが、とりあえずC++でもC#でもシリアライズ・デシリアライズ共に意図通りに動いている感じです(今のところは)。

*1:ソースコードが悪いのではなくて、そのままは使えないような特殊な環境ということで。

C#でMessagePackを使う

C++で書いたアプリとC#で書いたアプリの間でMessagePackを使ってデータをやりとりできないかな、とやってます。C++の方はなんとか使い方を覚えましたが、C#の方はC#自体ここ一ヶ月くらいしか経験がないせいもあって苦労してます。どこかに質問しようにも頭の中を整理しないと始まらないので、ここにメモします。

参考

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 結局数時間悩んだ問題はセミコロンを一つ削除することで対処できた。

ちなみにこれ、別ラインの同じソースでも同じ原因で同じように悩んで同じように修正してるはずなんだよね。その場でこちらのソースも修正すれば良かった。過去の俺もっと頑張れよ(無茶振り)。