RegistryKey.GetValue() を C++ でどう表現するか?

Win32 API のうち,よく使うものについてはラッパクラスを書いていこうと思っています.今は,C++ (Win32 API) か C# (.NET Framework) でコードを書くことが多いので,Win32 API のラッパクラスのインタフェースは .NET Framework に似せて定義していく事を考えています.

C# から C++ に移植する上で問題となりやすいものの一つに,「Object 型で返される関数を C++ でどう表現するか?」と言うものがあるように思います.例えば,ある特定のレジストリ値を取得するための C# の関数 (メソッド) の定義は以下のようになっています.

public Object GetValue(string name)

これを C++ で表現する方法は,パッと思いついた限り,以下の 3 つの方法があります.

  1. GetValueXXX() のように型によって別々の名前の関数を定義する.
  2. boost::any を使う.
  3. GetValue() をテンプレート関数にして特殊化する.

GetValueXXX() のように型によって別々の名前の関数を定義する

この方法は,Active Template Library (ATL) でも取られており,最もシンプルな方法になるかと思います.今回は別の解決策を取りましたが,この方法が最も無難かなぁと言う気はします.

boost::any を使う

C# の Object 型の代替手段として boost::any を使うと言うのも思いつきやすい方法の一つです.boost::any を使うと GetValue() の定義,および使用側のコードは以下のような形になります.

class RegistryKey {
public:
    typedef TCHAR char_type;
    typedef std::basic_string<TCHAR> string_type;
    typedef boost::any object_type;
    
    object_type GetValue(const string_type& name) {
        object_type dest;
        
        // 必要な処理 ...
        
        return dest;
    }
};

int main(int argc, char* argv[]) {
    RegistryKey subkey = Registry::CurrentUser().OpenSubKey(_T("foo"));
    DWORD value = boost::any_cast<DWORD>(subkey.GetValue(_T("bar")));
    return 0;
}

使用感は,この方法が最も C# の該当関数に近いものになるような気はします.

GetValue() をテンプレート関数にして特殊化する

最終的に,今回採用した方法はこれでした.この方法は,はじめテンプレート・メンバ関数は特殊化できないものと思い込んでいたので敬遠していたのですが,関数テンプレートの特殊化について - memologue を見ると,クラスのスコープ外に記述すれば可能なようです.

class RegistryKey {
public:
    typedef TCHAR char_type;
    typedef std::basic_string<TCHAR> string_type;
    
    template <class ObjectT>
    ObjectT GetValue(const string_type& name);
};

// DWORD, std::string 等で特殊化していく
template <>
inline DWORD RegistryKey::GetValue(const string_type& name) {
    // ...
}

int main(int argc, char* argv[]) {
    RegistryKey subkey = Registry::CurrentUser().OpenSubKey(_T("foo"));
    DWORD value = subkey.GetValue<DWORD>(_T("bar"));
    return 0;
}

まだ途中ですが,https://github.com/clown/psdotnet でこの方法でのラッパを公開しています.