Предельный тип аргументов для объединения BSTR - PullRequest
2 голосов
/ 29 марта 2019

Итак, я написал эту шаблонную переменную функцию ConcatBstr() для объединения нескольких строк BSTR.
Однако другие пользователи вызывают его с аргументами других типов, кроме BSTR. Хотя эту функцию также можно использовать для приема WCHAR, другие типы строк в действительности не совместимы с ней.

Q1 : Как правильно ограничить тип аргументов, принимаемых ConcatBstr(), только BSTR?

UINT LenSum() { // Stop condition
    return 0;
}

template <typename T, typename... Args>    // General case
UINT LenSum(T bstr, Args... args) {
    return SysStringLen(bstr) + LenSum(args...);
} 

void AppendBstr(WCHAR* dest) {  // Stop condition
    *dest = L'\0';  //Add the terminating zero. SysReAllocStringLen() allocated 1 more wchar_t for it already
}

template <typename T, typename... Args>     // General case
void AppendBstr(WCHAR* dest, T src, Args... args) {
    UINT n = SysStringLen(src);
    wmemcpy(dest, BSTR(src), n);
    AppendBstr(dest + n, args...);
}



BSTR ConcatBstr(BSTR* s) { return *s; } 

template <typename... ADDTHIS>
BSTR ConcatBstr(BSTR* dest, ADDTHIS... addthis) {   
    UINT n = SysStringLen(*dest);
    SysReAllocStringLen( dest, *dest, n +  LenSum(addthis...) );  //Call this expensive function only ONCE !
    AppendBstr( *dest + n, addthis...);
    return *dest;
}

int main(int argc, char* argv[]) { //Usage
    BSTR s1 = SysAllocString(L"Quick");
    BSTR s2 = SysAllocString(L"Fox");
    BSTR s3 = SysAllocString(L"Jumped");
    BSTR s4 = SysAllocString(L"Over");

    wcout << ConcatBstr(&s1, s2, s3, s4) << endl;
    //I know that these BSTRs need to be freed eventually
}

Q2 : Можно ли улучшить этот код, сохранив свойство вызова SysReAllocStringLen() только один раз?

1 Ответ

1 голос
/ 30 марта 2019

A1: Не уверен, что вы запрашиваете, так как компилятор уже отклоняет любую попытку передать несовместимый тип, поскольку создание экземпляра шаблона в какой-то момент завершится неудачно.Но вы можете использовать std::enable_if или static_assert, чтобы получить более описательное сообщение об ошибке.

A2: С C ++ 17 вы можете сократить код до одной функции, используявыражения сгиба и лямбда:

template<typename... ADDTHIS>
BSTR ConcatBstr(BSTR* dest, ADDTHIS... addthis)
{
    // use static_assert to get a more descriptive error message
    static_assert((std::is_same_v<ADDTHIS, BSTR> && ...), "tried to concant something else than BSTR");

    UINT n = SysStringLen(*dest);

    // use fold expression to sum up string lenghts
    SysReAllocStringLen(dest, *dest, n + (SysStringLen(addthis) + ...));

    // pointer to the next insertion
    WCHAR* ptr = *dest + n;

    // lambda appending the given string, incrementing ptr for the next invocation
    auto append = [&ptr](BSTR src)
    { 
        UINT n = SysStringLen(src);
        memcpy(ptr, src, n * sizeof(*ptr));
        ptr += n;
    };

    // use fold expressions to call `append` for every argument from left to right
    (append(addthis), ...);

    // append the zero terminator
    *ptr = L'\0';

    return *dest;
}

Обратите внимание, что лямбда захватывает вставку ptr по ссылке, поэтому при каждом вызове будет использоваться увеличенное значение предыдущего вызова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...