BREW API と intrusive_ptr の親和性
は最悪だと思うのです。
C での擬似的な継承関係は C++ の継承関係とは関係が無いので、型の変換が一切できないのです。
intrusive_ptr<IShell> shell; intrusive_ptr<IBase> base; shell = base; // 当然エラー base = shell; // エラー base = static_pointer_cast<IBase>(shell); // エラー base = shell.get(); // エラー base = static_cast<IBase*>(shell.get()); // エラー base = reinterpret_cast<IBase*>(shell.get()); // ようやく OK
というかそもそも intrusive_ptr_add_ref と intrusive_ptr_release も IBase* に変換できないので、必要になった BREW API の数だけ全部定義してやる必要があります。
void intrusive_ptr_add_ref(IBase* obj) { IBASE_AddRef(reinterpret_cast<IBase*>(obj)); } void intrusive_ptr_release_ref(IBase* obj) { IBASE_Release(reinterpret_cast<IBase*>(obj)); } void intrusive_ptr_add_ref(IShell* obj) { IBASE_AddRef(reinterpret_cast<IBase*>(obj)); } void intrusive_ptr_release_ref(IShell* obj) { IBASE_Release(reinterpret_cast<IBase*>(obj)); }
超めんどい。やってられない。
以下のように書けるのが理想です。
intrusive_ptr<IBase> base; intrusive_ptr<IShell> shell; intrusive_ptr<IDisplay> display; base = display; // OK shell = display; // エラー shell = static_pointer_cast<IShell>(base); // OK shell = static_pointer_cast<IShell>(display); // エラー
どれも C++ の継承関係があるなら全部当たり前なことなのですが、BREW API は C 互換なので仕方が無いのでしょうね……。
ということで、ゴリゴリと書いて頑張ってみました。
intrusive_ptr<> という汎用的なクラスで扱うのは諦めて brew_ptr<> という BREW 専用のクラスにしておいて、自前で BREW の継承関係を C++ の継承で定義したクラスを作って、BREW のクラスからその継承関係を調べるためのクラスを取得できるような traits を作ってやってます。
自前で継承関係を作ってるので、クラスを追加するのが面倒だったりとかするのですが、まあ強制キャストで押し通すよりはマシになったかなと。
以下実装
#include <AEE.h> // ↓boost から引っ張ってきたヘッダ #include <enable_if.hpp> #include <nullptr.hpp> #include <static_assert.hpp> namespace detail { struct IBaseHierarchy { }; template<class T> struct brew_traits { static const bool is_brew_interface = false; }; #define BREW_HIERARCHY(c, b) struct c##Hierarchy : public b##Hierarchy { } #define BREW_TRAITS(c) \ template<> \ struct brew_traits<c> \ { \ static const bool is_brew_interface = true; \ typedef c##Hierarchy hierarchy; \ } // 各 traits と継承関係を作る BREW_TRAITS(IBase); #define BREW_INFO(c, b) BREW_HIERARCHY(c, b); BREW_TRAITS(c) BREW_INFO(IShell, IBase); BREW_INFO(IDisplay, IBase); BREW_INFO(IModule, IBase); BREW_INFO(IApplet, IBase); // 以下適当に追加をば。 #undef BREW_INFO #undef BREW_TRAITS #undef BREW_HIERARCHY } // detail template<class T> class brew_ptr { private: typedef brew_ptr this_type; // T が BREW のインターフェースじゃないならエラーにする STATIC_ASSERT(detail::brew_traits<T>::is_brew_interface); public: typedef T element_type; brew_ptr() : p_(0) { } // new での生成と違って既に所有権が設定されていることが多く、 // implicit だといろいろ問題が出ちゃうので……。 explicit brew_ptr(T* p, bool add_ref = true) : p_(p) { if (p_ != nullptr && add_ref) IBASE_AddRef(reinterpret_cast<IBase*>(p_)); } template<class U> brew_ptr(const brew_ptr<U>& rhs) : p_(reinterpret_cast<U*>(rhs.get())) { // U(の hierarchy)から T(の hierarchy)に implicit 変換できないならエラーにする const typename detail::brew_traits<T>::hierarchy& test = typename detail::brew_traits<U>::hierarchy(); // 本物のポインタは reinterpret_cast で強制的に変換:p if (p_ != nullptr) IBASE_AddRef(reinterpret_cast<IBase*>(p_)); } brew_ptr(brew_ptr const & rhs): p_(rhs.p_) { if (p_ != nullptr) IBASE_AddRef(reinterpret_cast<IBase*>(p_)); } ~brew_ptr() { if (p_ != nullptr) IBASE_Release(reinterpret_cast<IBase*>(p_)); } template<class U> brew_ptr& operator=(const brew_ptr<U>& rhs) { this_type(rhs).swap(*this); return *this; } brew_ptr& operator=(const brew_ptr& rhs) { this_type(rhs).swap(*this); return *this; } brew_ptr& operator=(T* rhs) { this_type(rhs).swap(*this); return *this; } void reset() { this_type().swap(*this); } void reset(T* rhs) { this_type(rhs).swap(*this); } T* get() const { return p_; } T& operator*() const { ASSERT(p_ != nullptr); return *p_; } T* operator->() const { ASSERT(p_ != nullptr); return p_; } typedef T * this_type::*unspecified_bool_type; operator unspecified_bool_type() const { return p_ == nullptr ? nullptr : &this_type::p_; } bool operator! () const { return p_ == 0; } void swap(brew_ptr& rhs) { T* tmp = p_; p_ = rhs.p_; rhs.p_ = tmp; } T** address() { return &p_; } private: T * p_; }; template<class T, class U> inline bool operator==(const brew_ptr<T>& a, const brew_ptr<U>& b) { return a.get() == b.get(); } template<class T, class U> inline bool operator!=(const brew_ptr<T>& a, const brew_ptr<U>& b) { return a.get() != b.get(); } template<class T, class U> inline bool operator==(const brew_ptr<T>& a, U* b) { return a.get() == b; } template<class T, class U> inline bool operator!=(const brew_ptr<T>& a, U* b) { return a.get() != b; } template<class T, class U> inline bool operator==(T* a, const brew_ptr<U>& b) { return a == b.get(); } template<class T, class U> inline bool operator!=(T* a, const brew_ptr<U>& b) { return a != b.get(); } template<class T> inline bool operator==(const brew_ptr<T>& a, nullptr_t b) { return a.get() == nullptr; } template<class T> inline bool operator!=(const brew_ptr<T>& a, nullptr_t b) { return a.get() != nullptr; } template<class U> inline bool operator==(nullptr_t a, const brew_ptr<U>& b) { return nullptr == b.get(); } template<class U> inline bool operator!=(nullptr_t a, const brew_ptr<U>& b) { return nullptr != b.get(); } template<class T, class U> brew_ptr<T> static_pointer_cast(const brew_ptr<U>& p) { // U(の hierarchy)から T(の hierarchy)に static_cast できないならエラーにする const typename detail::brew_traits<U>::hierarchy test; const typename detail::brew_traits<T>::hierarchy& test2 = static_cast<const typename detail::brew_traits<T>::hierarchy&>(test); if (&test2 == &test2) { } // Warning 対策 return brew_ptr<T>(reinterpret_cast<T*>(p.get()), true); } // static_cast 以外はいらないでそ。 // brew_ptr<T> const_pointer_cast(const brew_ptr<U>& p) // brew_ptr<T> dynamic_pointer_cast(const brew_ptr<U>& p)
brew_ptr<IBase> base; brew_ptr<IShell> shell; brew_ptr<IDisplay> display; base = display; // OK shell = display; // エラー shell = static_pointer_cast<IShell>(base); // OK shell = static_pointer_cast<IShell>(display); // エラー