From: Jim Johnson on
is this C style coding?

I don't seem to see much C++ code in this way.

is this a bad programming practice?

code seem ugly coding this way.


=================
CATCHERROR(m_Cnn,0)

=================
#define CATCHERROR(ptr,a) catch(_com_error &e)\
{\
ErrorHandler(e,m_ErrStr);\
ptr=NULL;\
return a;\
}

From: Alex Blekhman on
"Jim Johnson" wrote:
> is this C style coding?
>
> I don't seem to see much C++ code in this way.
>
> is this a bad programming practice?
>
> code seem ugly coding this way.
>
> =================
> CATCHERROR(m_Cnn,0)
>
> =================
> #define CATCHERROR(ptr,a) catch(_com_error &e)\
> {\
> ErrorHandler(e,m_ErrStr);\
> ptr=NULL;\
> return a;\
> }


There is not enough information to say conclusively whether this
code is ugly or not. Although generally macros are evil, still
there are situatons where macros may be helpful.

From the code you provided I can see that `m_ErrStr' variable is
used. It suggests that the `CATCHERROR' macro is only a part of a
bigger exception handling machinery. Also, it is unclear what's
the purpose of the `ptr' parameter of the macro. It seems that
other than being nullified it's not used. If it is some kind of
cleanup, then it's definitely ugly C-style. One should use
destructors to clean resources that can be leaked during an
exception.

It looks that the `CATCHERROR' macro is used on a COM module
boundary to catch internal exceptions and translate them into
return values. Then probably it would be wiser to return actual
HRESULT of an error code (retrievable via e.Error() call) instead
of arbitrary zero value.

Alex


From: Giovanni Dicanio on

"Jim Johnson" <aopiyy001(a)yahoo.com> ha scritto nel messaggio
news:lgldv39oat9jm42ur9m2n974l4h6gr3cfn(a)4ax.com...

> =================
> CATCHERROR(m_Cnn,0)
>
> =================
> #define CATCHERROR(ptr,a) catch(_com_error &e)\
> {\
> ErrorHandler(e,m_ErrStr);\
> ptr=NULL;\
> return a;\
> }

The above is C++ code, because it uses 'catch' to catch exception
(_com_error &), and I believe it is not possible in pure C.

The CATCHERROR seems a preprocessor macro defined to avoid repeating
boiler-plate code.

Frankly speaking, if this macro is used wisely and not abused, I would not
define that bad code.
*Sometimes* preprocessor macros can come in handy.

e.g. I read some DirectShow SDK and COM code that uses HRCALL macro. It is
also available on MSDN.

<code>

// Macro that calls a COM method returning HRESULT value:
#define HRCALL(a, errmsg) \
do { \
hr = (a); \
if (FAILED(hr)) { \
dprintf( "%s:%d HRCALL Failed: %s\n 0x%.8x = %s\n", \
__FILE__, __LINE__, errmsg, hr, #a ); \
goto clean; \
} \
} while (0)

</code>

<code>

// Helper function that put output in stdout and debug window
// in Visual Studio:
void dprintf( char * format, ...)
{
static char buf[1024];
va_list args;
va_start( args, format );
vsprintf_s( buf, format, args );
va_end( args);
OutputDebugStringA( buf);
printf("%s", buf);
}

// Helper function to create a DOM instance:
IXMLDOMDocument * DomFromCOM()
{
HRESULT hr;
IXMLDOMDocument *pxmldoc = NULL;

HRCALL( CoCreateInstance(__uuidof(MSXML2::DOMDocument30),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXMLDOMDocument),
(void**)&pxmldoc),
"Create a new DOMDocument");

HRCALL( pxmldoc->put_async(VARIANT_FALSE),
"should never fail");
HRCALL( pxmldoc->put_validateOnParse(VARIANT_FALSE),
"should never fail");
HRCALL( pxmldoc->put_resolveExternals(VARIANT_FALSE),
"should never fail");

return pxmldoc;
clean:
if (pxmldoc)
{
pxmldoc->Release();
}
return NULL;
}
</code>


My 2 cents.

Giovanni


From: Giovanni Dicanio on

"Alex Blekhman" <tkfx.REMOVE(a)yahoo.com> ha scritto nel messaggio
news:uSZWmkvlIHA.3512(a)TK2MSFTNGP03.phx.gbl...

> There is not enough information to say conclusively whether this code is
> ugly or not. Although generally macros are evil, still there are situatons
> where macros may be helpful.

I do agree.


> One should use destructors to clean resources

Yes, in general RAII can help in building more quality code.
And I agree we should use RAII when possible.

However, RAII (and related smart pointers) may also have their fine points
and subtle aspects, e.g. in this code sample:

<pseudo-code>

some function or method
{
CoInitialize...

ComSmartPointer sp1;
ComSmartPointer sp2;
...
ComSmartPointer spN;

...
use COM objects via their associated smart pointers
...

... Assume that smart pointer destructors call Release()

CoUninitialize();
}

</pseudo-code>

The above code is wrong, because I believe that the smart pointer
destructors are called just before exiting the code block, so *first*
CoUninitialize is called, *then* the smart pointer destructors are called.
And that is not correct (because I think that CoUninitialize must be called
*after* all COM interfaces are Release'd).

This is a subtle thing.

*Explicit* Release() with a 'goto cleanup' statement (like some MSDN and
other SDK documentations do) would have made the code flow more clear, and
avoid the above bug.

<pseudo-code>

... if (FAILED(hr) )
goto cleanup;

...
.... if (FAILED(hr))
goto cleanup;

...

cleanup:
// Explicitly call Release here
...

</pseudo-code>


And IMHO a preprocessor macro like CHECK_HR (that checks if current HRESULT
is a failure code and jumps to 'cleanup' label accordingly) would be good in
that scenario, too.

Of course, there are workarounds for the aforementioned bug, too, e.g.
enclose the smart pointers into a { ... } block, and put
CoInitialize/CoUninitialize out of that block:

<pseudo-code>

{
CoInitialize...
{
... smart pointers defintition and usage...
}
CoUninitialize();
}

</pseudo-code>


Giovanni



From: Alex Blekhman on
"Giovanni Dicanio" wrote:
> However, RAII (and related smart pointers) may also have their
> fine points and subtle aspects, e.g. in this code sample:
>
> <pseudo-code>
>
> some function or method
> {
> CoInitialize...
>
> ComSmartPointer sp1;
> ComSmartPointer sp2;
> ...
> ComSmartPointer spN;
>
> ...
> use COM objects via their associated smart pointers
> ...
>
> ... Assume that smart pointer destructors call Release()
>
> CoUninitialize();
> }
>
> </pseudo-code>

Yes, this error is quite common. That's why I usually prefer to
make a simple class for COM initialization:

struct COMInitializer
{
COMInitializer(...)
{
CoInitialize(...);
}

~COMInitializer()
{
CoUninitialize();
}
};

some function or method
{
COMInitializer comInit;

...
}


Alex