есть следующий код:
#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 и на сколько это будет менее или более производительнее. Для раскрытия используется же рекурсия? Компилятор встроит этот код или будет постоянно вызывать рекурсию?
Можно ли его как-то переписать с использование 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);
}
Без использования ...
, обойтись можно:
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, которые квадратичны по числу аргументов функции).
Виртуальный выделенный сервер (VDS) становится отличным выбором
Имеется программа, которая берет из файла значения, записывает их в вектор типа структуры и затем по этим значениям рисует фигуру: поля структуры...
Правильно ли я понимаю, что C++, в отличии от C, запрещает неявное приведение между enum и int?
PID это идентификатор (13 бит) в MPEG-TS потоке (стриме), присваиваемый каждому элементарному транспортному пакету (ЭТП)