Search code examples
templatesvisual-c++mfcsafearray

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);
}
  • from is easy: BSTR.
  • to is easy: CStringArray.

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.


Solution

  • 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);
    }