2012-05-29 2 views
3

Я пытаюсь написать оболочку интеллектуального указателя (содержит boost shared_ptr или scoped_ptr или другую оболочку интеллектуального указателя); каждый тип обертки вводит немного дополнительной функциональности (например, использование журналов, ленивый init, проверка правильного порядка построения/уничтожения и т. д.), но я хочу, чтобы они были максимально невидимыми для пользователя (чтобы я мог обмениваться обертками просто изменив один typedef и, возможно, некоторый код конструктора, но ни один из кода использования).Обертка интеллектуальных указателей с заменой метода

Нормального использование тривиально:

template<typename T> 
class some_smart_ptr_wrapper 
{ 
public: 
    typedef typename T::value_type value_type; 
    ... 
    value_type* get() { /*do something magic*/ return m_ptr.get(); } 
    const value_type* get() const { /*do something magic*/ return m_ptr.get(); } 
    // repeat for operator->, operator*, and other desired methods 
private: 
    T m_ptr; 
}; 
typedef some_smart_ptr_wrapper< boost::shared_ptr<int> > smart_int; 
smart_int p; 

(Теперь другой код можно использовать p неотличим от shared_ptr, по крайней мере, для определенных операций, и я могу добавить дополнительные упаковщик, просто изменив ЬурейиЙ.)

Несколько оберток можно использовать одновременно, просто вложив их, как вы ожидали, и только код, который объявляет (а иногда и инициализирует) переменную, должен заботиться.

Иногда полезно иметь «базовый» общий/scoped_ptr из обертки (в частности, передать shared_ptr в качестве аргумента функции, которая не требует активации функциональности, добавленной оболочками); для одного слоя оберточной легко:

T& getPtr() { return m_ptr; } 
    const T& getPtr() const { return m_ptr; } 

Но это не масштабируется хорошо для нескольких слоев обертки (абонент должен сделать p.getPtr().getPtr().getPtr() правильное число раз).

То, что я хотел бы быть в состоянии сделать это объявить getPtr() таким образом, что:

  • если T реализует getPtr(), то она возвращает m_ptr.getPtr() (независимо от типа, который) ,
  • если T не реализует getPtr(), то он возвращает m_ptr.
  • он по-прежнему доступен как в созвездиях, так и в неконтактных ароматах, как и следовало ожидать.

Конечным результатом является то, что один вызов getPtr() из внешнего кода «поднимет» цепочку к исходному интеллектуальному указателю, поскольку это будет единственный, который не реализует getPtr().

Я уверен, что решение будет включать SFINAE и boost :: enable_if; У меня была небольшая игра, пытаясь заставить что-то подобное работать, но до сих пор не повезло.

Решения или полностью альтернативные подходы как приветствуются; обратите внимание, что я хотел бы, чтобы он работал как в VC++ 2008, так и в GCC (т. е. C++ 11, к сожалению).

+0

'value_type' должен быть' element_type', и вы должны иметь только один элемент 'get':' element_type * get() const; '. Указатель const не означает, что pointee является const. –

+0

Справедливые точки. Благодарю. – Miral

ответ

1

(1) Объявляет специальный TypeName член, который является уникальным внутри:

template<typename T> 
class some_smart_ptr_wrapper 
{ 
public: 
    typedef T smart_type; // <-- unique name 
... 
}; 

(2) обертывание getPtr() внутри функции шаблона обертки:

template<typename T> 
class some_smart_ptr_wrapper 
{ 
... 
public: 
    T& getPtr() { return m_ptr; } 

    typename My_sfinae<T>::RealType& getFinalPtr() 
    { return My_sfinae<T>::getPtr(m_ptr); } 
... 
}; 

(3) где My_sfinae находится как обычно:

template<typename T> 
struct void_ { typedef void type; }; 

template<typename T, typename = void> 
struct My_sfinae { 
    typedef T RealType; 

    static T& getPtr (T &p) { return p; } 
};      //^^^^^^^^^ business logic! 
template<typename T> 
struct My_sfinae<T, typename void_<typename T::smart_type>::type> { 
    typedef typename My_sfinae<typename T::smart_type>::RealType RealType; 

    static RealType& getPtr (T &p) 
    { return My_sfinae<typename T::smart_type>::getPtr(p.getPtr()); } 
}; 

Как видите, My_sfinae удаляет все ваши обертки рекурсивно и, наконец, вы остаетесь с окончательным m_ptr.

Here is the demo.

+2

Хорошее решение. Я бы предпочел класс признаков, а не навязчивую модификацию вообще (потому что он позволяет настраивать поведение на классах, которыми вы не владеете), но основной принцип SFINAE - это действительно хорошая идея. –

+0

Я не вижу, как это может сработать. Помните, что в shared_ptr T является фактическим типом, в обертке первого уровня T является shared_ptr , в обертке второго уровня T является first_level_wrapper > и т. Д. Вызов getPtr() на обертке второго уровня должен возвращать shared_ptr , который НЕ является T этого типа оболочки. – Miral

+0

@Miral, я упомянул в коде, что вам нужно использовать 'getFinalPtr()' вместо 'getPtr()'. Вы можете переименовать эти методы, чтобы получить желаемый результат. – iammilind

0

Ближайшая вещь, которая приходит мне на ум, - это статья, написанная Бьярне Страуступом под названием Wrapping C++ Member Function Calls.

Эта статья представляет собой простой, общее и эффективное решение старой проблемы «„оборачивания“» вызывает к объекту в парах приставкой и суффиксом кода. Решение также является неинтрузивным, применяется к существующим классам, позволяет использовать несколько пар префикс/суффикс, а может быть реализовано в 15 простых строках стандарта C++. Также представлена ​​надежная версия обертки . Требование эффективности подтверждается измерением.

+1

Я действительно не вижу, как это помогает OP, может быть, немного пример кода поможет? – Xeo

+0

Я могу предложить только сложнее. –

+0

Тогда, пожалуйста, просветите меня, поскольку OP не ищет способ обертывания вызовов функций с помощью префикса и кода суффикса (если я полностью не понял вопрос), он хочет рекурсивно вызывать определенные функции, пока не достигнет базового типа. – Xeo

1

Это, на самом деле, простая задача: просто использовать Перегрузки :)

template <typename T> 
boost::scoped_ptr<T>& get_pointer(boost::scoped_ptr<T>& sp) { return sp; } 

template <typename T> 
boost::shared_ptr<T>& get_pointer(boost::shared_ptr<T>& sp) { return sp; } 

// One overload per "wrapper" 
template <typename T> 
typename get_pointer_type<T>::type& get_pointer(some_wrapper<T>& p) { 
    return get_pointer(p.getPtr()); 
} 

Хитрость затем правильно специализироваться get_pointer_type для каждой оболочки.

template <typename T> 
struct get_pointer_type { typedef T type; }; 

template <typename T> 
struct get_pointer_type< some_wrapper<T> > { 
    typedef typename get_pointer_type<T>::type type; 
}; 
+0

Самое простое и лучшее решение для C++ 03, +1. – Xeo

+0

Btw, [здесь] (http://ideone.com/QQauU) - это гораздо более приятная версия C++ 11, которая даже немного похожа на совпадение шаблонов Haskell. Обратите внимание, что GCC не скомпилируется в этой версии, но Clang 3.1 компилируется просто отлично, что [кажется, намерение] (http://stackoverflow.com/questions/3744400/trailing-return-type-using-decltype-with- a-variadic-template-function # comment3959283_3744400), поскольку задействован пользовательский тип. – Xeo

+0

@Xeo: не знаю про самый простой (поскольку для обертки требуется как одна перегрузка, так и одна специализация), мне очень нравится решение iamilind; но я согласен с тем, что 'decltype' просто волшебство :) –