Andrew Truckle
Andrew Truckle

Reputation: 19197

Can this template function be adapted to account for the following method?

This question is a followon to this one:

This is my new template function:

template<typename from, typename to>
void CMSATools::ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems)
{
    from* pVals = nullptr;
    HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory

    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
        if (FAILED(hr))
            throw _com_error(hr);

        hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
        if (FAILED(hr))
            throw _com_error(hr);

        rItems.clear();
        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            rItems.push_back(pVals[i]);
        }
        hr = SafeArrayUnaccessData(psaItems);
        if (FAILED(hr))
            throw _com_error(hr);
    }
    else
    {
        throw _com_error(hr);
    }

    hr = SafeArrayDestroy(psaItems);
    if (FAILED(hr))
        throw _com_error(hr);
}

I have this other method that I would like to be able to use with the template function above:

void CMSATools::ConvertSAFEARRAY_BSTR(SAFEARRAY* psaStrings, CStringArray& rAryStrings)
{
    BSTR *pVals = nullptr;
    HRESULT hr = SafeArrayAccessData(psaStrings, (void**)&pVals); // direct access to SA memory

    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        hr = SafeArrayGetLBound(psaStrings, 1, &lowerBound);
        if (FAILED(hr))
            throw _com_error(hr);

        hr = SafeArrayGetUBound(psaStrings, 1, &upperBound);
        if(FAILED(hr))
            throw _com_error(hr);

        rAryStrings.RemoveAll();
        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            CString strPublisher(pVals[i]);

            rAryStrings.Add(strPublisher);
        }
        hr = SafeArrayUnaccessData(psaStrings);
        if (FAILED(hr))
            throw _com_error(hr);
    }
    else
    {
        throw _com_error(hr);
    }

    hr = SafeArrayDestroy(psaStrings);
    if (FAILED(hr))
        throw _com_error(hr);
}

The problem is that all of the other parameters were derived from std::list and we used push_back. In this instance, when it is of type CStringArray I need to use Add(...) (and also create the CString element first).

I see lots of suggestions here but it is not clear to me what the simplest approach should be.


It seems I should be able to is: std::is_same<from,CStringArray>::value to see if they are the same, but the answers seem to frown on using this approach.

Upvotes: 1

Views: 57

Answers (1)

dxiv
dxiv

Reputation: 17668

Complications in this case are the clear and push_back calls which CStringArray does not implement. While both can be solved generically as described in Templated check for the existence of a class member function? for example, a simpler approach can work to cover just the proposed case by using a couple of helper function templates.

// default implementations for <T, std::list<T>>
template<typename to>
void to_clear(to &rItems)
{   rItems.clear(); }

template<typename from, typename to>
void to_push_back(const from &rItem, to &rItems)
{   rItems.push_back(rItem); }

// specializations for <BSTR, CStringArray>
template<>
void to_clear(CStringArray &rItems)
{   rItems.RemoveAll(); }

template<>
void to_push_back(const BSTR &rItem, CStringArray &rItems)
{   rItems.Add(rItem); } // implicit CString conversion

// e.g.  ConvertSAFEARRAY<int, std::list<int>>
//       ConvertSAFEARRAY<IDiscussionItemPtr, ListDiscussionItems>
//       ConvertSAFEARRAY<BSTR, CStringArray>
template<typename from, typename to>
void ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems)
{
    from* pVals = nullptr;
    HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory

    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
        if (FAILED(hr))
            throw _com_error(hr);

        hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
        if (FAILED(hr))
            throw _com_error(hr);

        to_clear<to>(rItems);
        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            to_push_back<from, to>(pVals[i], rItems);
        }
        hr = SafeArrayUnaccessData(psaItems);
        if (FAILED(hr))
            throw _com_error(hr);
    }
    else
    {
        throw _com_error(hr);
    }

    hr = SafeArrayDestroy(psaItems);
    if (FAILED(hr))
        throw _com_error(hr);
}

Upvotes: 1

Related Questions