Серия COM-вызовов
От: disop Украина  
Дата: 14.04.03 09:25
Оценка:
Господа, я думаю многим приходится выполнять серию инициализаций COM-обьектов, при этом выполнение ветки кода надо прерывать при неуспешной инициализации хотя бы одного из них. Я встречал такие решения:


HRESULT hRes = S_OK;

hRes = COMOBJECT1.Initialize();
if ( SUCCEEDED(hRes) )
{
    hRes = COMOBJECT2.Initialize();
    if ( SUCCEEDED(hRes) )
    {
        hRes = COMOBJECT3.Initialize();
        ... и так далее
    }
}

Некрасиво...
Попадалось и такое:

HRESULT hRes = S_OK;

hRes = COMOBJECT1.Initialize();
if ( FAILED(hRes) )
    goto Err;
hRes = COMOBJECT2.Initialize();
if ( FAILED(hRes) )
    goto Err;
    ... и так далее

}

Еще хуже

Мой вариант такой инициализации:

HRESULT hRes = S_OK;

for(; ; ) 
{ 
    hRes = COMOBJECT1.Initialize();
    if ( FAILED(hRes) )
        break;
    hRes = COMOBJECT2.Initialize();
    if ( FAILED(hRes) )
        break;
    ... и так далее
    // Все ОК.
    break;
}
// Проверяем результат тут.
if (FAILED( hRes) 
{
    .....
}


Как вам такой вариант? Может гуру COM посоветуют что-то еще более красивое?

WBR, DisoP.
Re: Серия COM-вызовов
От: Ведмедь Россия  
Дата: 14.04.03 09:35
Оценка:
Здравствуйте, disop, Вы писали:

А почему не так?



void Check( HRESULT hr )
{
    if( FAILED( hr ) )
    {
      //вытаскиваем инфорамаци
      throw hr;//можно кидать свой
    }
}

...


try
{
  HRESULT hr
  Check( COMOBJECT1.Initialize() );
  Check( COMOBJECT2.Initialize() );
...

} catch( HRESULT hr )
{
  throw hr;
}
Да пребудет с тобой Великий Джа
Re: Серия COM-вызовов
От: Sergey Katsyuba  
Дата: 14.04.03 09:42
Оценка: +1 -1
Здравствуйте, disop.

Можно использовать исключения, они именно для таких случаев и созданы.

try
{
    hRes = COMOBJECT1.Initialize();
    if ( FAILED(hRes) )
        throw CMyExeception(hRes, "Ошибка 1");
    hRes = COMOBJECT2.Initialize();
    if ( FAILED(hRes) )
        throw CMyExeception(hRes, "Ошибка 2");
    ... и так далее
    // Все ОК.
}

}
catch (CMyExeception& e)
{
    //...
}
Re: Серия COM-вызовов
От: Аноним  
Дата: 14.04.03 10:15
Оценка: 6 (1)
Я всегда использую такой стиль:
HRESULT hr;
hr = MyObj->MyMethod();
if (FAILED(hr))
    return hr;


ЗЗЫ: Я не гуру
Re: Re: Exceptions красивее!
От: disop Украина  
Дата: 14.04.03 10:36
Оценка:
Здравствуйте, disop, Вы писали:

>> Может гуру COM посоветуют что-то еще более красивое?

Exceptions красивее, адназначна!
Re: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 14.04.03 11:10
Оценка: 23 (3)
Здравствуйте, disop, Вы писали:

[]

Можно вот так:

struct ExceptionalHresult
{
    ExceptionalHresult(HRESULT hr = S_OK)
        :    hr_(hr)
    {
        if (FAILED(hr))
            throw std::runtime_error("Failed HRESULT has been got.");
    }

    ExceptionalHresult& operator=(HRESULT hr)
    {
        if (FAILED(hr_ = hr))
            throw std::runtime_error("Failed HRESULT has been got.");
        return *this;
    }
};

HRESULT CoClass::Method()
{
    try
    {
        ExceptionalHresult hRes;
        hRes = COMOBJECT1.Initialize();
        hRes = COMOBJECT2.Initialize();
        return S_OK;
    }
    catch(const exception& e)
    {
        return ATL::AtlReportError(
            __uuidof(CoClass),
            e.what(),
            __uuidof(IMyInterface),
            E_FAIL);
    }
}
Re[2]: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 14.04.03 11:13
Оценка:
Здравствуйте, MaximE, Вы писали:

catch(const std::exception& e)
Re[2]: Серия COM-вызовов
От: Ocelot  
Дата: 14.04.03 12:29
Оценка:
Здравствуйте, Ведмедь, Вы писали:

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


В>А почему не так?



В>try

В>{
В> HRESULT hr
В> Check( COMOBJECT1.Initialize() );
В> Check( COMOBJECT2.Initialize() );
В>...

В>} catch( HRESULT hr )

В>{
В> throw hr;
В>}
В>[/ccode]

Да, с исключениями правильно и красиво — если только можно их использовать.
Доводилось писать COM-объекты, которые должен были быть предельно маленькими
по размеру. Пришлось отказаться от стандартного рантайма, а вместе с ним и от
исключений. Использовал конструкцию типа

do
{
hRes = COMOBJECT1.Initialize();
if ( FAILED(hRes) )
break;
hRes = COMOBJECT2.Initialize();
if ( FAILED(hRes) )
break;
}
while(false);

, и даже потом сделал макро-обертки, т.е.
#idef USE_RUNTIME
#define com_try try
#else
#define com_try do
#endif

Т.е. код потом можно было компилить и с исключениями, и без.
Re: Серия COM-вызовов
От: Plutonia Experiment Беларусь http://blogs.rsdn.org/ikemefula
Дата: 14.04.03 13:10
Оценка:
Здравствуйте, disop, Вы писали:

D>Господа, я думаю многим приходится выполнять серию инициализаций COM-обьектов, при этом выполнение ветки кода надо прерывать при неуспешной инициализации хотя бы одного из них. Я встречал такие решения:


Я делал так примерно:

int iRes = 0;

i += !SUCCEDED(pObject->SomeMethod());
i += !SUCCEDED(pObject->SomeMethod1());
i += !SUCCEDED(pObject->SomeMethod2());
i += !SUCCEDED(pObject2->SomeMethod());
i += !SUCCEDED(pObject2->SomeMethod1());
i += !SUCCEDED(pObject2->SomeMethod2());

return i ? S_OK : E_FAIL;


Но лучше делать чз исключения.
Re[2]: АпIIIbl6ка
От: Plutonia Experiment Беларусь http://blogs.rsdn.org/ikemefula
Дата: 14.04.03 13:12
Оценка:
Здравствуйте, Plutonia Experiment, Вы писали:

PE>
PE>int iRes = 0;

PE>i += !SUCCEDED(pObject->SomeMethod());
PE>i += !SUCCEDED(pObject->SomeMethod1());
PE>i += !SUCCEDED(pObject->SomeMethod2());
PE>i += !SUCCEDED(pObject2->SomeMethod());
PE>i += !SUCCEDED(pObject2->SomeMethod1());
PE>i += !SUCCEDED(pObject2->SomeMethod2());

PE>return i ? E_FAIL : S_OK;
PE>


PE>Но лучше делать чз исключения.
Re[2]: Серия COM-вызовов
От: Ведмедь Россия  
Дата: 14.04.03 13:14
Оценка: 8 (1)
Здравствуйте, Plutonia Experiment, Вы писали:

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


D>Господа, я думаю многим приходится выполнять серию инициализаций COM-обьектов, при этом выполнение ветки кода надо прерывать при неуспешной инициализации хотя бы одного из них. Я встречал такие решения:


PE>Я делал так примерно:


PE>
PE>int iRes = 0;

PE>i += !SUCCEDED(pObject->SomeMethod());
PE>i += !SUCCEDED(pObject->SomeMethod1());
PE>i += !SUCCEDED(pObject->SomeMethod2());
PE>i += !SUCCEDED(pObject2->SomeMethod());
PE>i += !SUCCEDED(pObject2->SomeMethod1());
PE>i += !SUCCEDED(pObject2->SomeMethod2());

PE>return i ? S_OK : E_FAIL;
PE>


Это не хорошо Потому что зачастую не имеет смысл ( или даже нельзя ) вызывать все методы, если первый сбоит.
Да пребудет с тобой Великий Джа
Re[3]: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 14.04.03 13:21
Оценка: +1
Здравствуйте, Ведмедь, Вы писали:

В>Это не хорошо Потому что зачастую не имеет смысл ( или даже нельзя ) вызывать все методы, если первый сбоит.


Точно. Закоротим:

HRESULT hr;
if (SUCCEDED(hr = pObject->SomeMethod())
    && SUCCEDED(hr = pObject->SomeMethod1())
    && SUCCEDED(hr = pObject->SomeMethod2())
    && SUCCEDED(hr = pObject2->SomeMethod())
    && SUCCEDED(hr = pObject2->SomeMethod1())
    && SUCCEDED(hr = pObject2->SomeMethod2())); // do nothing
return hr;


[expr.log.and]
&& guarantees left to right evaluation: the second operand is not evaluated if the first operand is false.

Re[3]: Серия COM-вызовов
От: disop Украина  
Дата: 14.04.03 13:22
Оценка:
Здравствуйте, Ведмедь, Вы писали:

PE>Я делал так примерно:


PE>
PE>int iRes = 0;

PE>i += !SUCCEDED(pObject->SomeMethod());
PE>i += !SUCCEDED(pObject->SomeMethod1());
PE>i += !SUCCEDED(pObject->SomeMethod2());
PE>i += !SUCCEDED(pObject2->SomeMethod());
PE>i += !SUCCEDED(pObject2->SomeMethod1());
PE>i += !SUCCEDED(pObject2->SomeMethod2());

PE>return i ? S_OK : E_FAIL;
PE>


В>Это не хорошо Потому что зачастую не имеет смысл ( или даже нельзя ) вызывать все методы, если первый сбоит.


Во-во-во, и я о том же! Поэтому исключения все-таки рулезь
Re[3]: Серия COM-вызовов
От: Plutonia Experiment Беларусь http://blogs.rsdn.org/ikemefula
Дата: 14.04.03 13:35
Оценка:
Здравствуйте, Ведмедь, Вы писали:

В>Это не хорошо Потому что зачастую не имеет смысл ( или даже нельзя ) вызывать все методы, если первый сбоит.


Я знаю, что нехорошо. Я же написал — лучше исключениями пользоваться.
Надо до конца читать
Re[4]: Серия COM-вызовов
От: Ведмедь Россия  
Дата: 14.04.03 13:36
Оценка:
Здравствуйте, Plutonia Experiment, Вы писали:

PE>Здравствуйте, Ведмедь, Вы писали:


В>Это не хорошо Потому что зачастую не имеет смысл ( или даже нельзя ) вызывать все методы, если первый сбоит.


PE>Я знаю, что нехорошо. Я же написал — лучше исключениями пользоваться.

PE>Надо до конца читать

Дык если исключениями не пользуешься, все равно не стоит вызывать лишние методы
Да пребудет с тобой Великий Джа
Re[2]: Серия COM-вызовов
От: EM Великобритания  
Дата: 02.09.03 17:15
Оценка:
ME>
ME>    catch(const exception& e)
ME>    {
ME>        return ATL::AtlReportError(
ME>            __uuidof(CoClass),
ME>            e.what(),
ME>            __uuidof(IMyInterface),
ME>            E_FAIL);
ME>    }
ME>


Так писать нельзя — AtlReportError юзает string conversion макросы, поэтому ее вызов в catch блоке чреват ...
Опыт — это такая вещь, которая появляется сразу после того, как была нужна...
Re[2]: Серия COM-вызовов
От: UnrealAlex Россия  
Дата: 02.09.03 17:40
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>
ME>struct ExceptionalHresult
ME>{
ME>    ExceptionalHresult(HRESULT hr = S_OK)
ME>        :    hr_(hr)
ME>    {
ME>        if (FAILED(hr))
ME>            throw std::runtime_error("Failed HRESULT has been got.");
ME>    }

ME>    ExceptionalHresult& operator=(HRESULT hr)
ME>    {
ME>        if (FAILED(hr_ = hr))
ME>            throw std::runtime_error("Failed HRESULT has been got.");
ME>        return *this;
ME>    }
ME>};
ME>[ccode]

Да, полностью за этот способ  :super: опробован временем на практике 
Что-то наподобие было у Бокса в "Effective COM"

[ccode]
struct HRX {
    HRX(void) { }
    HRX(HRESULT hr) { if (FAILED(hr)) throw hr; }
    HRX& operator=(HRESULT hr) 
    { if (FAILED(hr)) throw hr; return *this; }
};
Невозможное мы сделаем сегодня — чудо займет немного больше времени. /Аноним/
Re[3]: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 02.09.03 17:46
Оценка:
Здравствуйте, EM, Вы писали:

ME>>
ME>>    catch(const exception& e)
ME>>    {
ME>>        return ATL::AtlReportError(
ME>>            __uuidof(CoClass),
ME>>            e.what(),
ME>>            __uuidof(IMyInterface),
ME>>            E_FAIL);
ME>>    }
ME>>


EM>Так писать нельзя — AtlReportError юзает string conversion макросы, поэтому ее вызов в catch блоке чреват ...


_alloca
...
There are restrictions to explicitly calling _alloca in an exception handler (EH). EH routines that run on x86-class processors operate in their own memory frame: They perform their tasks in memory space that is not based on the current location of the stack pointer of the enclosing function. The most common implementations include Windows NT structured exception handling (SEH) and C++ catch clause expressions. Therefore, explicitly calling _alloca in any of the following scenarios results in program failure during the return to the calling EH routine:

* Windows NT SEH exception filter expression: __except (_alloca () )
* Windows NT SEH final exception handler: __finally {_alloca () }
* C++ EH catch clause expression

However, _alloca can be called directly from within an EH routine or from an application-supplied callback that gets invoked by one of the EH scenarios previously listed.


Т.е. _alloca можно безопасно вызывать не из самого обработчика, а из функции, вызываемой обработчиком.

P.S. Не понятно, что они имеют в виду говоря про "catch clause expression", но судя по всему это что-то типа:
catch(int* p = static_cast<int*>(_alloca(sizeof(int)))) // "catch clause"
{
    // "handler"
}


... хотя это бессмыслено.
Re[3]: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 02.09.03 17:52
Оценка:
Здравствуйте, UnrealAlex, Вы писали:

UA>Да, полностью за этот способ опробован временем на практике

UA>Что-то наподобие было у Бокса в "Effective COM"

Я сам его всегда пользую, когда пишу com-обертки для своих плюсовых компонентов (тем более, что эти компоненты легко могут бросить наследника std::exception)
Re[4]: Серия COM-вызовов
От: MaximE Великобритания  
Дата: 02.09.03 17:54
Оценка:
Здравствуйте, MaximE, Вы писали:

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


ME>>>
ME>>>    catch(const exception& e)
ME>>>    {
ME>>>        return ATL::AtlReportError(
ME>>>            __uuidof(CoClass),
ME>>>            e.what(),
ME>>>            __uuidof(IMyInterface),
ME>>>            E_FAIL);
ME>>>    }
ME>>>


EM>>Так писать нельзя — AtlReportError юзает string conversion макросы, поэтому ее вызов в catch блоке чреват ...


ME>

ME>_alloca
ME>...
ME>There are restrictions to explicitly calling _alloca in an exception handler (EH). EH routines that run on x86-class processors operate in their own memory frame: They perform their tasks in memory space that is not based on the current location of the stack pointer of the enclosing function. The most common implementations include Windows NT structured exception handling (SEH) and C++ catch clause expressions. Therefore, explicitly calling _alloca in any of the following scenarios results in program failure during the return to the calling EH routine:

ME>* Windows NT SEH exception filter expression: __except (_alloca () )
ME>* Windows NT SEH final exception handler: __finally {_alloca () }
ME>* C++ EH catch clause expression

ME>However, _alloca can be called directly from within an EH routine or from an application-supplied callback that gets invoked by one of the EH scenarios previously listed.


ME>Т.е. _alloca можно безопасно вызывать не из самого обработчика, а из функции, вызываемой обработчиком.


ME>P.S. Не понятно, что они имеют в виду говоря про "catch clause expression", но судя по всему это что-то типа:

ME>
ME>catch(int* p = static_cast<int*>(_alloca(sizeof(int)))) // "catch clause"
ME>{
ME>    // "handler"
ME>}
ME>


ME>... хотя это бессмыслено.


The following only applies to the MS implementation of EH, I'm not
familiar with enough with any other implementation to comment.

When you throw an exception, the exception object is allocated on the
stack (please don't ask why, I can only guess.) The stack cannot be
physically unwound before entering a catch because the thrown object
might be rethrown from a catch. The stack pointer is restored after the
catch exits normally.

This means that _alloca can be called from within the catch, but the
result cannot be used outside the catch.

There is one other constraint that is really a bug. With VC6, you can't
call _alloca from within a try block. The stack pointer is not restored
correctly after taking an exception. This has been fixed in VC7.

Jason Shirk
VC++ Compiler Team

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.