Увеличивать указатель при распаковке переменных аргументов с помощью лямбда-выражения

#c #c 17

#c #c 17

Вопрос:

У меня есть следующий код, в котором есть функция VariadicWrite , и эта функция принимает указатель, который она может изменять, увеличивая его, чтобы он указывал на другую ячейку памяти в зависимости от того, сколько данных в нее записано:

 #include <iostream>
#include <cstring>

template<typename T>
T Read(void* amp;ptr) noexcept
{
    T resu<
    memcpy(amp;result, ptr, sizeof(result));
    ptr = static_cast<T*>(ptr)   1;
    return resu<
}

template<typename T>
void Write(void* amp;ptr, T result) noexcept
{
    memcpy(ptr, amp;result, sizeof(result));
    ptr = static_cast<T*>(ptr)   1;
}

template<typename... Args>
auto VariadicWrite(void*amp; ptr, Argsamp;amp;... args)
{
    (Write(ptr, args), ...); //unpack Args
    return 0;
}

int main()
{
    void* ptr = malloc(1024);
    memset(ptr, 0, 1024);
    void* temp = ptr;

    VariadicWrite(ptr, 1, 2, 3);
    
    std::cout<<Read<int>(ptr)<<"n";
    std::cout<<Read<int>(ptr)<<"n";
    std::cout<<Read<int>(ptr)<<"n";

    free(temp);
    return 0;
}
 

Проблема здесь в том, что код выводится, 0, 0, 0, 0 если я использую: void*amp; ptr .
Если я это сделаю void* ptr , он распечатается 0, 1, 2, 3 , но ptr указатель никогда не увеличивается.

Как я могу изменить ptr указатель VariadicWrite на? Я думал, что void*amp; это сработало бы, но в данном случае это не так: S

Комментарии:

1. Пожалуйста, добавьте c тег в сообщения C в дополнение к тегу версии.

2. Я добавил тег. Также сделал код компилируемым, чтобы каждый мог его протестировать.

3. Это здорово, спасибо.

4. Я подозреваю, что в вашем коде есть UB, потому что в результате я получил разные результаты. godbolt.org/z/YPWMWK

5. Я не знаю, насколько вы знакомы с c , но не используйте malloc and free практически при любых обстоятельствах. Кроме того, предпочитайте интеллектуальные указатели необработанным указателям.

Ответ №1:

Проблема в том, что ваш VariadicWrite() вызов будет изменен ptr , чтобы указывать на конец ваших записанных данных. Затем вы вызываете Read() , не сбрасывая указатель обратно в начало, поэтому вы считываете нули из неинициализированной части вашего буфера, которая следует за уже записанными данными.

Вставьте ptr = temp; между write и read и посмотрите, исправит ли это.

Причина void* ptr , по которой это не работает, заключается в том, что каждый вызов Write(ptr, ...) будет увеличивать локальную копию аргумента в области действия Write() функции. Переменная ptr in VariadicWrite() не изменяется после вызова to Write() , поэтому при следующем вызове будет использоваться то же значение.

Если вы измените значение VariadicWrite(void* ptr, …) и Write(void*amp; ptr, …) , вы можете получить желаемое поведение. Но я бы предположил, что это плохая идея.

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

Как правило, лучше избегать неконстантных ссылок, потому что они часто приводят к подобным ошибкам. Я предлагаю вернуть новый указатель вместо изменения аргумента.

 template<typename T>
void* Write(void* ptr, const Tamp; arg)
{ 
    return static_cast<T*>(ptr)   1;
}

template<typename... Args>
void* WriteV(void* ptr, Argsamp;amp;... args)
{
    ((ptr = Write(ptr, args)), ...);
    return ptr;
}
 

Комментарии:

1. Вы, вероятно, имеете в виду «return static_cast<T*>(ptr) 1;» в функции записи.