очень умные указатели
От: shurik.  
Дата: 13.09.05 07:38
Оценка:
есть такая проблема
как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн.

в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы.
соответсвенно для них тоже хочется смартпоинтеры, причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать public-методы не описанные в интерфейсе)

попробовал CComPtr
возникло две проблемы:
1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.
2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.

основная проблема под номером 2.
Наверняка ведь кто-то сталкивался с подобной задачей?
может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?
Re: очень умные указатели
От: Ivan Россия www.rsdn.ru
Дата: 13.09.05 07:52
Оценка: 2 (1) +1
Здравствуйте, shurik., Вы писали:

S>как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн.

S>в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы.
S>соответсвенно для них тоже хочется смартпоинтеры,

если есть желание использовать эти смартпойнтеры — нужно в stdafx.h сделать import своей собственной библиотеки типов (и убрать #include в модулях cpp генерируемого по idl заголовочного файла с объявлениями интерфейсов)


>причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать >public-методы не описанные в интерфейсе)


чтобы правильно использовать этот прием, нужно не забыть добавить в карту интерфейсов псевдо-интерфейс с произвольным IID (обычно берут CLSID класса), который возвращает указатель на класс.

S>попробовал CComPtr

S>возникло две проблемы:
S>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.

покажи код, который не компилируется


S>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.


и _com_ptr_t (typedef'ы для которого генениурет import) и CComPtr одинаково подходят для решения твоей задачи. Для CComPtr можно написать класс, который будет проверять возвращаемый HRESULT и кидать исключение, если FAILED(HRESULT). Тогда разницы между CComPtr и _com_ptr_t не будет практически никакой, не считая того, что _com_ptr_t более "тяжеловесный".

S>может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?


гораздо более простой и работающий способ — присваивать результат вызова метода экземпляру специального класса, который проверит HRESULT и кинет исключение для FAILED(HRESULT)
Re[2]: очень умные указатели
От: shurik.  
Дата: 13.09.05 08:44
Оценка:
Здравствуйте, Ivan, Вы писали:

I>Здравствуйте, shurik., Вы писали:


I>если есть желание использовать эти смартпойнтеры — нужно в stdafx.h сделать import своей собственной библиотеки типов (и убрать #include в модулях cpp генерируемого по idl заголовочного файла с объявлениями интерфейсов)


от этого я только ушёл, потому что такой подход пораждает кучу проблем. (этот код писал не я, я только избавлялся от импорта собственной тлбэхи, может там что-то было не правильно, но уже принято решение так не делать и я этого решения отменить не могу)

I>чтобы правильно использовать этот прием, нужно не забыть добавить в карту интерфейсов псевдо-интерфейс с произвольным IID (обычно берут CLSID класса), который возвращает указатель на класс.


вот это идея, спасибо. Как-нибудь обязательно попробую.

S>>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.

I>покажи код, который не компилируется
CCoClass::InternalMethod(CAnohterInternalCoClass *i_p) {
    // member of CCoClass - CComPtr<CAnohterInternalCoClass> m_ptr;
    m_ptr = i_p;  // error
    m_ptr = CComPtr<CAnohterInternalCoClass>(i_p);  // error
}

а вообще проблему уже решил (отнаследовался от CComPtr и переопределил оператор присваивания)

S>>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.


I>и _com_ptr_t (typedef'ы для которого генениурет import) и CComPtr одинаково подходят для решения твоей задачи. Для CComPtr можно написать класс, который будет проверять возвращаемый HRESULT и кидать исключение, если FAILED(HRESULT). Тогда разницы между CComPtr и _com_ptr_t не будет практически никакой, не считая того, что _com_ptr_t более "тяжеловесный".


здесь стоп. Т.е. возможно написать класс сматпоинтера, чтобы в использовании он вёл себя как сгенерённый импортом?
т.е. например
CMyComPtr<CCoClass> ptr;
// здесь кинется исключение, т.к. метод возвратит E_FAIL, а поинтер это поймёт и кинет исключение?
ptr->ReturnEFail();


если ты это имел ввиду, то подскажи плз идею как это сделать.
ведь если пергружать оператор -> то в нём мы возвращаем raw-указатель а как нам после этого получить возвращённый HRESULT...
немного я над этим подумал пока на работу ехал... никаких идей не пришло

I>гораздо более простой и работающий способ — присваивать результат вызова метода экземпляру специального класса, который проверит HRESULT и кинет исключение для FAILED(HRESULT)


это мне не подходит
т.к. код будет примерно следующий
// может произойти исключение
ptrToExternalComObject->Method();
// чтобы оно в случае неудачи кинулось и здесь, надо писать так
EnsureSuccess(ptrToInternalObject->Method());


т.е. постоянно надо думать — какой объект мы используем, если внутренний то ещё парится и писать EnsureSuccess...
вобщем баги такое любят...
Re[3]: очень умные указатели
От: TheThief Россия  
Дата: 13.09.05 10:41
Оценка:
Директива #import генерирует обертки вокруг интерфейсов, внутри методов оберток всеравно происходит проверка HRESULT.

Как-то мне лень было везде писать EnsureSuccess(ptrToInternalObject->Method()) и я написал обертку для HRESULT, переопределил метод присваивания и в нем проверял присваеваемое значение и выкидывал exception в случае ошибки. Далее создавал переменную CMyHresult hr и если какой-то метод интерфейса возвращает значение типа HRESULT я просто присваиваю его переменной.


    CMyHresult hr;

    pSomeInterface->SomeVoidMethod();

    hr = pOtherInterface->SomeHresultMethod();



Вроде и смотрится лучше, и работает также (в смысле exception выкидывает).
Re[4]: очень умные указатели
От: shurik.  
Дата: 13.09.05 11:00
Оценка:
Здравствуйте, TheThief, Вы писали:

TT>Директива #import генерирует обертки вокруг интерфейсов, внутри методов оберток всеравно происходит проверка HRESULT.


это я понимаю 8)

TT>
TT>    CMyHresult hr;

TT>    pSomeInterface->SomeVoidMethod();

TT>    hr = pOtherInterface->SomeHresultMethod();
TT>

TT>Вроде и смотрится лучше, и работает также (в смысле exception выкидывает).

да, согласен смотрится получше...
но всё равно хочу вызов методов идентичный импортовским смартпоинтерам
Re[3]: очень умные указатели
От: Ivan Россия www.rsdn.ru
Дата: 13.09.05 11:06
Оценка:
Здравствуйте, shurik., Вы писали:

S>Здравствуйте, Ivan, Вы писали:


I>>Здравствуйте, shurik., Вы писали:


S>вот это идея, спасибо. Как-нибудь обязательно попробую.

интересно, как у тебя получается сейчас обойтись без этого
ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?

S>это мне не подходит

S>т.к. код будет примерно следующий
S>
S>// может произойти исключение
S>ptrToExternalComObject->Method();
S>// чтобы оно в случае неудачи кинулось и здесь, надо писать так
S>EnsureSuccess(ptrToInternalObject->Method());
S>


я имел в виду не вызов функции EnsureSuccess, а использование оператора присваивания (см. соседний пост) — код получается короче и стройнее

S>т.е. постоянно надо думать — какой объект мы используем, если внутренний то ещё парится и писать EnsureSuccess...

S>вобщем баги такое любят...

полностью согласен. нужно либо использовать CComPtr (опция raw_interfaces_only для import), либо _com_ptr_t, но смешивать их — ни к чему.
Re[4]: очень умные указатели
От: shurik.  
Дата: 13.09.05 15:12
Оценка:
Здравствуйте, Ivan, Вы писали:

I>интересно, как у тебя получается сейчас обойтись без этого

I>ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?

а я просто создаю не CoCreateInstance а так
CComPtr<CCoClass> ptr = new CComObject<CCoClass>

так что ничего кастить не надо 8)

I>я имел в виду не вызов функции EnsureSuccess, а использование оператора присваивания (см. соседний пост) — код получается короче и стройнее

см. мой ответ на прошлый пост 8)
хочу без дополнительных переменных...
хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)
Re: очень умные указатели
От: shurik.  
Дата: 13.09.05 15:17
Оценка:
вопрос к модераторам...
суть вопроса уехала от кома в сторону С++...
мне туда новый пост написать или можно эту ветку перенести в сишный форум?

кое-что получилось...
не знаю что делать дальше
может у кого идеи возникнут...

написал два таких класса (к кому отношения не имеют)

template<typename T, typename TVal>
class CDummy {
private:
    T *m_p;

public:
    explicit CDummy(T *i_p) : 
        m_p(i_p) {
    }
    ~CDummy() {
    }
    operator T* () {
        return m_p;
    }
    T* operator->() {
        return m_p;
    }
};

template<typename T, typename TVal>
class CVerySmartPtr {
protected:
    T*      m_p;

public:
    int i;
    CVerySmartPtr() {
        m_p = NULL;
    }
    CVerySmartPtr(T *i_p) :
        m_p (i_p) {
    }
    ~CVerySmartPtr() {
        if(m_p != NULL)
            delete m_p;
    }
    CDummy<T, TVal> operator->() {
        return CDummy<T, TVal>(m_p);
    }
};


и их использование

CVerySmartPtr<CTestClass, unsigned int> ptr(new CTestClass);
ptr->Method();


суть в том что деструктор CDummy вызывается после отработки метода Method
как бы теперь получить HRESULT...
Re[2]: очень умные указатели
От: ssm Россия  
Дата: 14.09.05 07:40
Оценка: +1
Здравствуйте, shurik., Вы писали:

S>как бы теперь получить HRESULT...


никак
Re[5]: очень умные указатели
От: ssm Россия  
Дата: 14.09.05 07:43
Оценка:
Здравствуйте, shurik., Вы писали:


S>хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)


#import — это не смарт поинтер
простым смартпоинтером — не обойтись
тебе необходимо грузить библиотеку типов, и делать врапперы для интерфейсов, для которых уже и использовать _com_ptr
Re: очень умные указатели
От: novik Россия  
Дата: 14.09.05 10:10
Оценка:
Здравствуйте, shurik., Вы писали:

S>есть такая проблема

S>как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн.

S>в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы.

S>соответсвенно для них тоже хочется смартпоинтеры, причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать public-методы не описанные в интерфейсе)

S>попробовал CComPtr

S>возникло две проблемы:
S>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.
S>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.

S>основная проблема под номером 2.

S>Наверняка ведь кто-то сталкивался с подобной задачей?
S>может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?

По моему опыту, я использую CoCreateinstance внутри сервера только для синхронизации вызова методов объектов через границы апартментов. А так стараюсь не обрезать себе функциональность методами интерфейса, а создаю полноценные классы типа CComObject<CMyClass>.

А чтобы решить вторую проблему можно создать производный класс
class
CMyClass : CComObject<CMyClass>

и переопределить ту же виртуальную функцию, только с нужной проверкой


class CMyClass : CComObject<CMyClass>
{
   public  STDMETHOD (Method) (...)
          {
             HRESULT hr = CMyClass::Metod (...)
             if FAILED (hr)
               ...
             return HR;
          }
}
Re[2]: очень умные указатели
От: novik Россия  
Дата: 14.09.05 10:25
Оценка:
Извиняйте за ошибки в коде, пишу без проверки, но смысл я думаю понятен?
Re[5]: очень умные указатели
От: Ivan Россия www.rsdn.ru
Дата: 14.09.05 10:48
Оценка:
Здравствуйте, shurik., Вы писали:

S>Здравствуйте, Ivan, Вы писали:


I>>интересно, как у тебя получается сейчас обойтись без этого

I>>ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?

S>а я просто создаю не CoCreateInstance а так

S>
S>CComPtr<CCoClass> ptr = new CComObject<CCoClass>
S>

S>так что ничего кастить не надо 8)

этого мало допустим, что компонент CCoClass поддерживает интерфейс ICoClass, тогда имея указатель на ICoClass* получить по нему CCoClass* уже нельзя, без dynamic_cast'а. т.е. такой код не сработает
ICoClass* pIClass;
CComPtr<CCoClass> spCoClass(pIClass);


типичный способ решения проблемы — завести псевдо-интерфейс
BEGIN_COM_MAP(CCoClass)
    COM_INTERFACE_ENTRY_IID(CLSID_CoClass, CCoClass)
END_COM_MAP()


тогда имея указатель на ICoClass ты можешь получить указатель на экземпляр класса так:
template<class T, class U>
inline CComPtr<T> get_raw_ptr(U* p)
{
    CComPtr<T> spT;
    p->QueryInterface( T::GetCLSID(), (void**)&spT);
    return spT;
}



насчет неоднозначного преобразования —
CCoClass::InternalMethod(CAnohterInternalCoClass *i_p) {
    // member of CCoClass - CComPtr<CAnohterInternalCoClass> m_ptr;
    m_ptr = i_p;  // error
    m_ptr = CComPtr<CAnohterInternalCoClass>(i_p);  // error
}

компилятор прав, так как класс CAnohterInternalCoClass нельзя однозначно преобразовать к IUnknown*, правильно получать нужный указатель в том числе и на CAnohterInternalCoClass нужно с помощью QI — см. выше функцию get_raw_ptr


S>хочу без дополнительных переменных...

S>хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)

тогда лучше всего было бы использовать импорт собственной библиотеки типов, т.к. добиться такого же поведения, которое обеспечивают стандартные врапперы будет проблематично — помимо проверки HRESULT врапперы выполняют часто преобразование типов параметров — BSTR<->_bstr_t и т.п. Могут быть специфические ошибки, связанные с тем, что в одном случае вызов через _com_ptr_t имеет дело с врапперами _variant_t, _bstr_t, а вызов через твой смартпойнтер — с BTSR и VARIANT

и по поводу смартпойнтера CVerySmartPtr —
> суть в том что деструктор CDummy вызывается после отработки метода Method
как бы теперь получить HRESULT...

получить можно "нечестным" способом — возвращаемое значение будет находиться в регистре eax. но
— этот код не может работать гарантированно для разных версий компилятора и флажков компиляции
— нет нормального способа проверить тип возвращаемого значения. если метод вернет не HRESULT, а ULONG (AddRef) или void — с проверкой eax может получиться очень нехорошо.


ИМХО, два лучших варианта такие:
— везде использовать _com_ptr_t — импортировать свою библиотеку типов
— везде использовать CComPtr и класс для ловли ислкючений, который будет кидать его в операторе присваивания (кстати,в этом случае ты можешь контролировать — нужно ли кидать исключение или нет)
Re[3]: очень умные указатели
От: shurik.  
Дата: 14.09.05 11:20
Оценка:
Здравствуйте, novik, Вы писали:

N>Извиняйте за ошибки в коде, пишу без проверки, но смысл я думаю понятен?


смысл понятен, спасибо.
Re[6]: очень умные указатели
От: shurik.  
Дата: 14.09.05 11:24
Оценка:
I>как бы теперь получить HRESULT...

I>получить можно "нечестным" способом — возвращаемое значение будет находиться в регистре eax. но

I>- этот код не может работать гарантированно для разных версий компилятора и флажков компиляции
I>- нет нормального способа проверить тип возвращаемого значения. если метод вернет не HRESULT, а ULONG (AddRef) или void — с проверкой eax может получиться очень нехорошо.

я тоже думал так сделать (верней просто попробовать, ессно такой бы код я не оставил)
однако деструктор CDummy поганит в Debug'е eax...

I>ИМХО, два лучших варианта такие:

I>- везде использовать _com_ptr_t — импортировать свою библиотеку типов
I>- везде использовать CComPtr и класс для ловли ислкючений, который будет кидать его в операторе присваивания (кстати,в этом случае ты можешь контролировать — нужно ли кидать исключение или нет)

выбираю второй вариант!
Re[7]: очень умные указатели
От: Ivan Россия www.rsdn.ru
Дата: 14.09.05 13:22
Оценка: 14 (3)
Здравствуйте, shurik., Вы писали:

S>я тоже думал так сделать (верней просто попробовать, ессно такой бы код я не оставил)

S>однако деструктор CDummy поганит в Debug'е eax...

в образовательных целях можно сделать деструктор CDummy — naked и написать код вручную:
template<typename T, typename TVal>
__declspec(naked) CDummy<T, TVal>::~CDummy()
{
        HRESULT hr; 
        __asm
        {
               push ebp
               mov ebp, esp
               sub esp, LOCAL_SIZE
               mov hr, eax
        }
        check( hr );
        __asm
        {
               mov esp, ebp
               pop ebp 
               ret 
        }
}
Re[8]: очень умные указатели
От: shurik.  
Дата: 14.09.05 13:45
Оценка:
Здравствуйте, Ivan, Вы писали:

I>Здравствуйте, shurik., Вы писали:



skip..

круто!
очень круто, спасибо!
только в релизе не работает 8)
Re[9]: очень умные указатели
От: Ivan Россия www.rsdn.ru
Дата: 14.09.05 14:21
Оценка:
Здравствуйте, shurik., Вы писали:

S>круто!

S>очень круто, спасибо!
S>только в релизе не работает 8)

а что именно не работает ?
в приведенном выше коде есть пара багов:
— правильно __LOCAL_SIZE, а не LOCAL_SIZE
— в зависимости от реализации функции check может потребоваться сохранять значения некоторых регистров. есть соглашение о том, значения каких регистров должны оставаться неизменными после вызова функции (ebp, esi,edi и т.п. — точный список не скажу)
Re[10]: очень умные указатели
От: shurik.  
Дата: 15.09.05 07:56
Оценка:
Здравствуйте, Ivan, Вы писали:

I>Здравствуйте, shurik., Вы писали:


S>>круто!

S>>очень круто, спасибо!
S>>только в релизе не работает 8)

I> а что именно не работает ?

I>в приведенном выше коде есть пара багов:
I>- правильно __LOCAL_SIZE, а не LOCAL_SIZE

не мог бы рассказать что значит __LOCAL_SIZE?

I>- в зависимости от реализации функции check может потребоваться сохранять значения некоторых регистров. есть соглашение о том, значения каких регистров должны оставаться неизменными после вызова функции (ebp, esi,edi и т.п. — точный список не скажу)


давно хотел с этим поподробней познакомиться...
не подскажешь где про это почитать можно? (в смысле по каким ключевым словам погуглить можно)
Re[10]: очень умные указатели
От: shurik.  
Дата: 15.09.05 08:07
Оценка:
Здравствуйте, Ivan, Вы писали:

I>Здравствуйте, shurik., Вы писали:


S>>круто!

S>>очень круто, спасибо!
S>>только в релизе не работает 8)

I> а что именно не работает ?


разобрался почему не работает...
Method был реализован в описании класса...
т.е. инлайновский был
вынес реализации Method в срр файл и всё сразу стало круто.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.