C++ COM Error Handling

C++, Error Handling No Comments »

After a long hiatus, I wrote a bit of code in C++ over the last two days. While I haven’t yet internalized a set of rules for error handling in C++ (see my previous posts about error handling A C/C++ Error Handling Discipline: Part 1, A C/C++ Error Handling Discipline: Part 2, A C/C++ Error Handling Discipline: Part 3, and On C++ Error Handling), I decided to use C++ exceptions in this application. Furthermore, since I was using MSXML, a lot of the error handling information was contained in IErrorInfo objects. To this end, I wrote the following macro:

#define ThrowComErrorIfFailed(x) \\
    do { \\
        HRESULT hr = (x); \\
        if (FAILED(hr)) { \\
            CComPtr<IErrorInfo> spErrorInfo; \\
            BOOL hasErrorInfo = (::GetErrorInfo(0, &spErrorInfo) == S_OK); \\
            _com_raise_error(hr, hasErrorInfo ? spErrorInfo : 0); \\
        } \\
    } while (0)

This macro uses the GetErrorInfo() function to retrieve the IErrorInfo object and _com_raise_error() to throw a _com_error class as the exception. (By the way, this macro can—and probably should—be written as an inline function. Old habits die hard, I guess.)

When I first wrote this macro, I thought it might be general enough to use in any COM application. Unfortunately, I was wrong. There’s no guarantee that the GetErrorInfo() call corresponds to the most recent COM call, as there’s no guarantee that it will be cleared or reset if the COM call doesn’t support IErrorInfo. A proper macro would first test if the COM object supports IErrorInfo by querying for ISupportErrorInfo and calling ISupportErrorInfo::InterfaceSupportsErrorInfo(). Only if these tests succeed would the macro then proceed to call GetErrorInfo().

To this end, I wrote the following code:

void ThrowComErrorIfFailed
    (
    HRESULT hr,
    IUnknown* pUnk,
    REFIID iid
    )
{
    if (FAILED(hr)) {
        bool hasErrorInfo = false;
        CComPtr<IErrorInfo> spErrorInfo;

        if (pUnk)
        {
            CComQIPtr<ISupportErrorInfo> spSupportErrorInfo(pUnk);
            if (spSupportErrorInfo) {
                if (spSupportErrorInfo->InterfaceSupportsErrorInfo(iid) == S_OK) {
                    if (::GetErrorInfo(0, &spErrorInfo) == S_OK) {
                        hasErrorInfo = true;
                    }
                }
            }
        }

        _com_raise_error(hr, hasErrorInfo ? spErrorInfo : 0);
    }
}

Note that to use this function, you must provide the COM object upon which you made the call (pUnk) and the exact interface of the object (iid). Because I used ATL’s CComPtr extensively, I also wrote the following utility function:

template <class T>
void ThrowComErrorIfFailed
    (
    HRESULT hr,
    const CComPtr<T>& comPtr
    )
{
    ThrowComErrorIfFailed(hr, comPtr, __uuidof(T));
}

This experience led me to the following observation: In order to correctly use IErrorInfo, you must know what object generated it. This means that if you call a COM object which implements ISupportErrorInfo, the call fails, and you return only the HRESULT failure code, you have lost the IErrorInfo information completely. Yet another reason to use exceptions, I guess…

Visual C++ 6.0 ifstream Inanity

C++ No Comments »

If you use ifstream from Visual C++ 6.0, beware of the behavior of the constructor and open member function. Consider the following code snippet:

const char* fileName = ...;
ifstream ifs(fileName);  // equivalent to ifstream ifs; ifs.open(fileName);

If fileName didn’t previously exist, the ifstream constructor will create it as a 0-byte file! If you don’t want the file to be created, use the ios::nocreate flag:

ifstream ifs(fileName, ios::in | ios::nocreate);

Whoever originally thought this was a good idea should be slapped.

Parallel Programming

Programming No Comments »

Recently I watched MSDN: Channel 9’s interview of Turing Award winner Jim Gray (the videos are here and here). In the context of massively parallel computation, he approvingly cited Google’s MapReduce paper, which I then read. MapReduce describes how Google uses the functional programming concepts map and reduce to allow a developer to write serial code but have it execute in parallel on their enormous computing farms.

I suspect we will see functional programming concepts further creep into mainstream languages as language and compiler designers address the challenges presented in Herb Sutter’s paper The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software. Functional programming seems (to my untrained eye) that it would be far easier to automatically parallelize.

I also suspect that as hardware parallelization increases, we will gladly trade off the performance of individual nodes (the serial path of execution) if it results in increased parallelization. In other words, traditional software optimization will be almost completely disregarded as nearly all efforts will concentrate on increasing concurrency. As this happens, the debates over the performance of native vs. managed code will seem quaint. Then again, I think the debates pretty much already are.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in