1

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.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

1 Answers1

1

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);
}
IInspectable
  • 46,945
  • 8
  • 85
  • 181
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • Hi! Thank you for your sample code with explanations which I have now sucessfully managed to interface into my class. I updated all instances of my method with the new one and it is working well. :) This means that I have one more safe array conversion function to try and adapt. I will try using the suggestions in this answer and if I get stuff I will ask a new question. – Andrew Truckle Jan 06 '21 at 08:48
  • I have encountered problems trying to compile the final function using similar approach. I put the question here: https://stackoverflow.com/questions/65592912/i-tried-to-add-a-new-template-helper-function-to-my-class-and-now-i-get-a-lnk200 – Andrew Truckle Jan 06 '21 at 09:44