Передача встроенного временного класса в C ++ должна быть константой. Как обойти это

Я хочу написать переносимый код на C ++ 11 для разных процессоров (на самом деле MCU). Поскольку некоторые ЦП не поддерживают чтение данных программы напрямую через адресное пространство памяти (например, Atmel AVR), мне нужно решение, которое вызывает функцию либо с прямым адресом, либо с указателем потока, созданным на заказ, для чтения данных через некоторые внешние место хранения.

Рассмотрим этот код как пользовательскую библиотеку:

class IStream
{
    public: virtual char ReadChar();
};

class ConstMemoryStream : public IStream
{
    const char* Position;

    public: ConstMemoryStream(const char* startAddress)
    {
        Position = startAddress;
    }

    public: char ReadChar() override
    {
        return *Position++;
    }
};

void Send(char data) { } // Send data to serial port

Теперь я хочу реализовать функцию, которая принимает либо адрес памяти, либо поток для чтения данных из:

// const parameter is needed here, otherwise error: invalid initialisation of non-const reference of type 'IStream&' from an rvalue of type 'IStream'
void PrintMessage(const IStream& stream)
{
    while (true) // TODO: end condition
        //Send(stream.ReadChar());  // this gives an error because i need to use a const parameter: passing 'const IStream' as 'this' argument discards qualifiers
        Send( ((IStream*)&stream)->ReadChar() );  // this works, this actually bypass the error above. IS THIS OK?????
}

void PrintMessage(char* address); // overload to use memory instead of stream. implementation not important here

Затем я хочу вызвать PrintMessage с помощью Stream, но этот поток должен быть создан встроенным и больше не нужен вне функции PrintMessage:

int main(void)
{
    // Requirement: ConstMemoryStream needs to be created and passed INLINE PrintMessage
    PrintMessage(ConstMemoryStream("Hello!")); // This works only if i put const in PrintMessage parameter.
}

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

Send( ((IStream*)&stream)->ReadChar() );

Это в основном делает параметр неконстантным, чтобы избежать ошибки. Но есть ли лучшее решение сделать это на законных основаниях?

Сам экземпляр потока не может быть константным, потому что он увеличивает его положение внутри, но С ++ требует передать его как константу, потому что это встроенная временная переменная, которая всегда рассматривается как rvalue.

Я не вижу никакого вреда в том, что временная переменная изменяет сама себя, после того, как функция PrintMessage вернет ее, она все равно отбрасывается.

В конце концов я хочу сделать следующее:

#ifdef CPU_AVR
    #define CSTR(str) ConstMemoryStream(PROGMEM str) // the PROGMEM attribute puts the text in a separate space not accessible in regular memory
#elif defined CPU_SAM
    #define CSTR(str) (char*)str
#endif

int main2(void)
{
    // If the CPU does not support direct address mapping to it's FLASH space, pass a stream instead of a direct memory pointer
    PrintMessage(CSTR("Hello"));
}

Есть идеи, как это сделать правильно, не отбрасывая ошибку? Или текущий код, приведенный выше, приемлем?


person Bigjim    schedule 27.10.2020    source источник
comment
Поддерживает ли ваш компилятор C ++ 11 или новее?   -  person Sam Varshavchik    schedule 27.10.2020
comment
Да, это C ++ 11 (обновил мой вопрос)   -  person Bigjim    schedule 27.10.2020
comment
const IStream & stream, почему вы делаете его const? просто возьмите реф и все нормально работает. Это не связано с AVR, поскольку, насколько мне известно, STL недоступен для AVR. Если у вас есть STL для AVR, дайте мне знать!   -  person Klaus    schedule 27.10.2020
comment
@anastaciu: сама строка является константой, но поток, который читает константную строку, не является константой, что разрешено. Мой вопрос не в том, чтобы удалить const из строки, а из потока.   -  person Bigjim    schedule 27.10.2020
comment
@Bigjim, ответ Сэма, кажется, решает вашу проблему, не так ли?   -  person anastaciu    schedule 27.10.2020
comment
@Bigjim, конечно, вам нужно сохранить квалификаторы const, чтобы правильно передать такую ​​строку.   -  person anastaciu    schedule 27.10.2020
comment
@anastaciu Действительно, && - идеальное решение. Просто попробовал и компилируется без ошибок и предупреждений   -  person Bigjim    schedule 27.10.2020
comment
@Bigjim: да, вы также можете передать сконструированный объект в качестве параметра, но это лучше, потому что он решает потенциальную область видимости и проблемы владения.   -  person anastaciu    schedule 27.10.2020
comment
@anastaciu Действительно, передача сконструированного объекта не была вариантом, потому что мне нужно использовать его в строке с макросом.   -  person Bigjim    schedule 27.10.2020


Ответы (2)


В C ++ 11 вы можете просто взять ссылку на rvalue в качестве параметра.

void PrintMessage(IStream && stream)

Ссылки rvalue будут привязываться к временным библиотекам и в этом контексте будут в основном неотличимы от ссылок lvalue.

person Sam Varshavchik    schedule 27.10.2020
comment
Это именно то, что я искал. Спасибо! - person Bigjim; 27.10.2020

Либо у вас неправильный интерфейс, либо вы неправильно используете. В настоящее время вы const_cast просматриваете свой поток. Если вы когда-нибудь передадите const объект IStream, это будет неопределенное поведение.

Или:

class IStream
{
    public: virtual char ReadChar() const;
};

Or:

void PrintMessage(IStream& stream)
{
    while (true) // TODO: end condition
        Send(stream.ReadChar());  
}

void PrintMessage(IStream&& stream)
{
    while (true) // TODO: end condition
        Send(stream.ReadChar());  
}

int main(void)
{
    PrintMessage(ConstMemoryStream("Hello!")); 
}
person Caleth    schedule 27.10.2020
comment
На самом деле, я знаю, что мое использование было неправильным, поэтому у меня возник вопрос (как это сделать правильно). IStream && - это решение, как и во втором примере. Спасибо! - person Bigjim; 27.10.2020