boost::shared_ptrでHMODULEなどを管理する

追記:

これが動くのは、そもそも、HMODULEが、ポインター型であることを前提としている。HMODULEがポインターであることを隠すために、わざわざHMODULEという名前を使っているというのに。このコードは、ポータビリティに問題があると言える。

http://cpplover.blogspot.com/2010/02/blog-post_8598.html

そういえば、HMODULEがポインタかどうか一切気にしていなかったのですが、ポインタでなければ使えないですねこれ。まぁ、スマートポインタなのだから、ポインタじゃないものを管理するのはどうか、ということでしょうか。

unique_ptrは名前しか知らなかったのでこの使い方は初めて知りましたが、こんな便利な使い方があるんですね。速くunique_ptrが使える環境に移りたい……。

元エントリ

DLLの動的ロードを行うときに:

  • LoadLibraryしたハンドルをFreeLibraryするのが面倒
  • デストラクタでFreeLibraryするラッパを書けばいいんじゃね
  • ラッパクラスを書くのも面倒!

というわけで、shared_ptrを使おう、と思い立ったのですが、そのまま使用するとshared_ptrが持つ方がHMODULE*になってしまい、色々都合が悪くなります。

boost::shared_ptr<HMODULE> dll(::LoadLibrary(_T("foo.dll")), &::FreeLibrary);

上記のように書くと、コンパイルも通りません。

エラー	1	error C2440: '初期化中' : 'HINSTANCE__ *' から 'HMODULE *' に変換できません。
エラー	2	error C2439: 'boost::shared_ptr<T>::px' : 指定されたメンバは初期化できません。

こういう時は、type_traitsのremove_pointerを使うと良いらしいです。

boost::shared_ptr<boost::remove_pointer<HMODULE>::type> dll(::LoadLibrary(_T("foo.dll")), &::FreeLibrary);

GetProcAddressなど使用するときに毎回getメンバ関数を呼ぶのも気持ち悪いので、その辺りのラッパ関数は作っておいた方が良さそうですね。また、型名が長くなるのでtypedefもした方が良いのでしょう。

こんな感じ?

#include <boost/shared_ptr.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <windows.h>

typedef boost::shared_ptr<boost::remove_pointer<HMODULE>::type> dll_handle;

dll_handle load_library(TCHAR const* fileName)
{
  return dll_handle(::LoadLibrary(fileName), &::FreeLibrary);
}

struct get_proc_address {
  get_proc_address(dll_handle const& handle, char const* name)
    : handle(handle), name(name)
  {}

  template<class F>
  operator F() const {
    return reinterpret_cast<F>(::GetProcAddress(handle.get(), name));
  }

private:
  dll_handle const& handle;
  char const* name;
};

int _tmain(int argc, _TCHAR* argv[])
{
  dll_handle dll = load_library(_T("foo.dll"));
  assert(dll);

  void (__stdcall * hoge)() = get_proc_address(dll, "hoge");
  hoge();
}