Variadic templates C++

133
11 сентября 2019, 19:50

есть следующий код:

#define concatenateStrings(destinationString, lenghtDestinationString, ...) \
     __concatenateStrings__(destinationString, lenghtDestinationString, ##__VA_ARGS__, NULL)
inline void __concatenateStrings__(char* destinationString, std::size_t lenghtDestinationString, const char* sourceStrings, ...) 
{
    va_list stringArguments;
    va_start(stringArguments, sourceStrings);
    std::fill(destinationString, destinationString + lenghtDestinationString, 0);
    for (const char* currentString = sourceStrings; currentString; currentString = va_arg(stringArguments, const char*))
        strcat_s(destinationString, lenghtDestinationString, currentString);
    va_end(stringArguments);
}

Можно ли его как-то переписать с использование Variadic Templates и на сколько это будет менее или более производительнее. Для раскрытия используется же рекурсия? Компилятор встроит этот код или будет постоянно вызывать рекурсию?

Answer 1

Можно ли его как-то переписать с использование Variadic Templates, и на сколько это будет менее или более производительнее

Разница должна быть минимальной. Правильно написанный шаблон медленнее точно не будет.

Для раскрытия используется же рекурсия?

Совершенно не обязательно использовать рекурсию.

В соседнем ответе уже предложили довольно простое решение. Вот более оптимизированный вариант, с проверкой на неправильные типы аргументов:

template <
    typename ...P,
    typename = std::enable_if_t<(std::is_convertible_v<P, const char *> && ...)>
>
void concatenateStrings(char *dest, std::size_t dest_size, const P &... params)
{
    dest_size--;
    auto lambda = [&](const char *str) -> bool
    {
        std::size_t str_size = std::strlen(str);
        bool no_more_space = 0;
        if (str_size >= dest_size)
        {
            no_more_space = 1;
            str_size = dest_size;
        }
        std::memcpy(dest, str, str_size);
        dest += str_size;
        dest_size -= str_size;
        return no_more_space;
    };
    (lambda(params) || ...);
    std::memset(dest, 0, dest_size+1);
}
Answer 2

Без использования ..., обойтись можно:

template< typename ... T >
inline void concatenateStrings(char* destinationString, std::size_t lenghtDestinationString, T ... strings) 
{
    std::fill(destinationString, destinationString + lenghtDestinationString, 0);
    const char* stringsArray[] = { strings ... };
    for (const char* currentString : stringsArray )
        strcat_s(destinationString, lenghtDestinationString, currentString);
}

Можно записать еще короче:

template< typename ... T >
inline void concatenateStrings(char* destinationString, std::size_t lenghtDestinationString, T ... strings) 
{
    std::fill(destinationString, destinationString + lenghtDestinationString, 0);
    for (const char* currentString :  { strings ... } )
        strcat_s(destinationString, lenghtDestinationString, currentString);
}

Но, в этом случае, будем получать сложно читаемые сообщения об ошибках, в случае если аргументы функции имеют тип отличный от const char*.

Как видишь, можно обойтись без рекурсии. В данном случае, компилятор с высокой вероятностью встроит код и развернет цикл (в рекурсивной реализации - тоже), Но в общем случае, встраивание зависит от сложности функции.

По производительности - примерно тоже самое, что в исходном варианте (т.е. разница много меньше затрат на strcat_s, которые квадратичны по числу аргументов функции).

READ ALSO
С++ ссылка на временный объект

С++ ссылка на временный объект

Хочу разобраться в двух следующих вопросах:

157
Откуда взялось значение

Откуда взялось значение

Имеется программа, которая берет из файла значения, записывает их в вектор типа структуры и затем по этим значениям рисует фигуру: поля структуры...

128
Отличия между enum в C и в C++

Отличия между enum в C и в C++

Правильно ли я понимаю, что C++, в отличии от C, запрещает неявное приведение между enum и int?

120
ffmpeg Как узнать PID для AVPacket?

ffmpeg Как узнать PID для AVPacket?

PID это идентификатор (13 бит) в MPEG-TS потоке (стриме), присваиваемый каждому элементарному транспортному пакету (ЭТП)

118