2012年1月19日木曜日

見逃していた Windows 上でのコンパイルエラー

仕事でミスを犯しました。自分は普段 Linux 上で開発をしており、ビルドもテストも自分の環境でパスしたのでコードをコミットしたところ、後で「Yuki、Windows上 でコンパイルエラー出てるよ」とメールが飛んできました。これには思わずエッ?となりました。さらにはコードベースを汚してるもんだから冷汗たらたら。エラーメッセージがあさっての方向を指していて調査に時間がかかりましたが原因はこういうことでした。簡単な例ですが、

MyClass.hpp
#ifndef MYCLASS_HPP_INCLUDED
#define MYCLASS_HPP_INCLUDED

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

class Empty; // forward declaration

class MyClass
{
public:
    void Foo(Empty *e) {}; // OK with g++ and with VC++

    void Bar(boost::shared_ptr<Empty> e) {}; // OK with g++ and with VC++

    void Baz(boost::scoped_ptr<Empty> e) {}; // OK with g++ but NOT with VC++
};

#endif

Main.cpp
#include "MyClass.hpp"

int main()
{
    MyClass obj;
    return 0;
}
気になって、帰宅してから自分の環境で試してみたところ、Windows Vista、Visual C++ 2008 Express Edition、boost 1.40.0 でも同様のコンパイルできない現象がみられました。Ubuntu 10.10、gcc 4.5.5、boost 1.42.0 ではコンパイルを通してくれました。

Smart Pointers は生のポインタのように使える上、生のポインタにつきまとう面倒を人に代わって管理してくれるという利点があり(基本的には。当然落とし穴も)、それは #include と forward declaration の関係にも通じると思いこんでいました。上の例で、Visual C++ でコンパイルを通すには、MyClass.hpp で #include <Empty.hpp> をしないとダメなようです。それを避けるためにポインタと forward declaration で済ませようとしているのに、うーん、仕事場の Visual C++ と 自宅の Visual C++ 2008 Express Edition ではそれを見逃してくれませんでした。

MyClass.hpp のような空の(implicit)inline 関数が並んだ一見用途が不明なクラスが登場するシナリオなんてあるのかと不思議に思うかもしれませんが、テストの際に Mock クラスとして使いたいときに上記のような MyClass を用意することがあります。

どこかで pointee の サイズを調べようとしてエラーが飛んできてるのかもしれませんが、なぜ Visual C++ ではそういう扱いなのかまだ根本のところで分かっていないので、各種の Smart Pointer の実装と照らし合わせてこれから調べていくところです。