Я сталкиваюсь со следующей ошибкой в зависимости от того, какой тип я выбираю для переменной.
/home/zak/Development/Arduino/generated_examples/ArduinoIoTCloud_Basic/thingProperties.h: In function 'void initProperties()':
/home/zak/Development/Arduino/libraries/ArduinoIoTCloud/src/ArduinoIoTCloud.h:116:64: error: cannot bind non-const lvalue reference of type 'int16_t& {aka int&}' to an rvalue of type 'int16_t {aka int}'
#define addProperty( v, ...) addPropertyReal(v, #v, __VA_ARGS__)
^
/home/zak/Development/Arduino/generated_examples/ArduinoIoTCloud_Basic/thingProperties.h:32:16: note: in expansion of macro 'addProperty'
ArduinoCloud.addProperty(potentiometer, 2, Permission::Read).publishOnChange(10);
^
In file included from /home/zak/Development/Arduino/generated_examples/ArduinoIoTCloud_Basic/ArduinoIoTCloud_Basic.ino:17:0:
/home/zak/Development/Arduino/libraries/ArduinoIoTCloud/src/ArduinoIoTCloud.h:158:15: note: initializing argument 1 of 'Property& ArduinoIoTCloudClass::addPropertyReal(int16_t&, String, int, Permission)'
Property& addPropertyReal(int16_t& property, String name, int tag, Permission const permission);
^~~~~~~~~~~~~~~
Вот тело addPropertyReal()
:
Property& ArduinoIoTCloudClass::addPropertyReal(int16_t& property, String name, int tag, Permission const permission)
{
Property* p = new CloudWrapperInt16(property);
return addPropertyReal(*p, name, tag, permission);
}
Проблема возникает, когда new
CloudWrapperInt16
:
class CloudWrapperInt16 : public CloudWrapperBase {
private:
int16_t &_primitive_value,
_cloud_value,
_local_value;
public:
CloudWrapperInt16(int16_t& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {}
...
^^ Как видите, _primitive_value
— это отсылка.
Как ни странно, это работает, когда я передаю настоящий int
или int16_t
. Однако я сталкиваюсь с этой ошибкой всякий раз, когда передаю эквивалентное значение, которое должно быть преобразовано в int16_t
, например short
.
УСПЕХ (int16_t
): (компилируется без предупреждений)
int16_t potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
УСПЕХ (int
): (компилируется без предупреждений)
int potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
FAIL (short
): (ошибка показана выше)
short potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
Обновлено: Изначально я забыл упомянуть, что этот конкретный пример компилируется для 16-битной платформы. Итак,
int
,short
иint16_t
должны быть одинакового размера.
Как ни странно, это работает, когда я передаю short
или int16_t
. Однако я сталкиваюсь с аналогичной ошибкой всякий раз, когда передаю значение int
.
УСПЕХ (int16_t
): (компилируется без предупреждений)
int16_t potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
УСПЕХ (short
): (компилируется без предупреждений)
short potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
FAIL (int
): (аналогичная ошибка, показанная выше)
int potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
Может кто-нибудь объяснить, что именно здесь происходит с преобразованием типов?
Я ищу способ, совместимый с POSIX, чтобы компилятор мог обращаться с ними одинаково, или способ улучшить код, чтобы он мог работать. Любое решение необходимо будет компилировать для нескольких архитектур (16-бит, 32-бит и т. д.), а также работать с несколькими цепочками инструментов (avr-gcc
, arm-none-eabi-g++
и т. д.).
ПРИМЕЧАНИЕ. У меня нет контроля над базой кода; это большой проект с открытым исходным кодом. Мне действительно нужно хирургическое решение, потому что чем шире работа по рефакторингу, тем труднее будет добиться принятия PR.
Это гигантская библиотека с открытым исходным кодом. Я не верю, что это возможно. В качестве альтернативы я предоставил код из сообщения об ошибке в его источник. Мгновенное голосование против, не пытаясь понять, не приветствуется. Я не верю, что эту проблему можно решить с помощью Годболта, иначе я бы сделал это сам. Должно быть очевидно, что это проблема «преобразования типов», и я предоставил весь код, связанный с происхождением типа и его преобразованием.
@Zak Что неясно в этом сообщении об ошибке «невозможно связать неконстантную ссылку lvalue типа 'int16_t& {aka int&}' с rvalue типа 'int16_t {aka int}'??
Путаница, возникающая из-за того, что на самом деле он может без предупреждения привязать int
(или int16_t
), но не short
. Есть какой-то нюанс в типах, который мне не понятен. В данном примере это компиляция для 16-битной платформы.
«Как ни странно, это работает, когда я передаю short
или int16_t
. Однако я сталкиваюсь с аналогичной ошибкой всякий раз, когда передаю значение int
». -- В этом повороте нет ничего странного. Вы по-прежнему получаете ошибку, когда тип должен быть приведен к int16_t
(как вы заметили для первого случая: «всякий раз, когда я передаю значение, которое [...] должно быть приведено к int16_t
»). Разница в том, что в этом сценарии int16_t
является псевдонимом short
, тогда как в первом примере это был псевдоним int
.
«Это гигантская библиотека с открытым исходным кодом. Я не верю, что это возможно». -- Как сказал Йода: «Размер не имеет значения». Это вполне возможно. Отбросьте вещи, которые не имеют значения. В сообщении об ошибке указывается проблема как «аргумент 1», поэтому отбросьте остальные параметры. Проблема заключается в вызове функции, поэтому тело не имеет значения; отбросьте это и введите возвращаемый тип void
. void addProperty(int16_t&) {}
. Тогда вам просто нужны сайты вызовов, чтобы продемонстрировать три способа вызова функции. Это быстро и легко: int main() { int16_t int16_works = 1; addProperty(int16_works); }
. Можете ли вы не использовать это?
«Я не верю, что это возможно». В заголовке вашего вопроса спрашивается, почему это не компилируется short s; int16_t& r = s;
. Но это даже неверно: в вашем сообщении об ошибке говорится, что вы привязываете неконстантную ссылку lvalue к rvalue. И если вы действительно не можете дать нам минимальный воспроизводимый пример , то какими именно волшебными ясновидящими, по вашему мнению, мы являемся, чтобы мы могли телепатически читать вашу кодовую базу?
На самом деле, Неконстантные ссылки на lvalue не могут быть связаны с lvalue другого типа должны быть дубликатами, но я думаю, что им не хватает деталей о том, что short
и int
являются разными типами, даже если они эквивалентны (то есть одинаковое количество битов). ).
@JaMiT Вас обманули, ошибка говорит: «'int16_t& {aka int&}' для rvalue типа 'int16_t {aka int}'», которое не привязано к другому типу.
@PasserBy Если вы имеете в виду мой пример минимальный воспроизводимый пример, мои извинения, но комментарии недостаточно длинные. Поэтому я сократил int main() { int16_t int16_works = 1; int int_fails = 2; short short_works = -3; addProperty(int16_works); addProperty(int_fails); addProperty(short_works); }
(с ошибкой второго вызова функции в 32-битной системе) до того, на основе чего, как мне казалось, можно было бы построить пример.
@JaMiT Я имел в виду сообщение об ошибке, в котором, по-видимому, говорилось, что тип в порядке, но категория значений неверна, и это моя ошибка. Судя по всему, старый gcc выдает неверные сообщения об ошибках.
Помимо того, что AVR является чрезвычайно ограниченным в ресурсах MCU, непригодным для динамического распределения (и C++...), существует множество технических причин вообще не использовать динамическое распределение: Почему мне не следует использовать динамическое распределение памяти во встроенных системах?
Полностью согласен, но, к сожалению, я не контролирую этот проект OSS. Я просто пытаюсь распутать беспорядок, который обнаружил.
Я прочитал предложенный дубликат, и это заставило меня задуматься, существует ли временная переменная, созданная во время приведения (например, от short
к int16_t
), которая в конечном итоге разрушит механизм ссылок и создаст проблему, подобную той, которую я вижу.
ПРИМЕЧАНИЕ. Этот ответ приветствуется для улучшения и остается исключительно в качестве основы для других. Он основан на эмпирических наблюдениях, а не на копании исходного кода рассматриваемых компиляторов или исследовании ISO.
Похоже, что псевдонимы компилятора avr-gcc
int16_t
как int
(или наоборот), а short
определяются отдельно. В то время как компилятор arm-none-eabi-gcc
использует псевдонимы int16_t
как short
(или наоборот).
Успех можно объяснить тем фактом, что они фактически относятся к одному и тому же типу данных, поэтому приведение или преобразование не требуется. Таким образом, сбои возникают из-за того, что компилятор считает необходимым преобразование или приведение типов.
После того как приведение произошло, создается временный объект. Очевидно, что временная ссылка не может быть привязана к неконстантной ссылке (что имеет смысл в сообщении об ошибке), поскольку ссылка не будет иметь связанного с ней хранилища и, следовательно, не может быть изменена во время выполнения.
Если вы изо всех сил пытаетесь понять это, подумайте о приведении целого числа к вещественному. Было бы невозможно обновить базовое целое число путем изменения значения с плавающей запятой.
В моем случае решение, которое мне было нужно, заключалось в дальнейшем шаблонизации моих целочисленных классов и функций. В конечном итоге это абстрагировало все целочисленные операции в единую общую реализацию.
short
и int
искажаются как два разных типа, а именно short
искажается как s
и int
искажается как i
.
Возьмите следующий пример кода:
#include <stdint.h>
int fun_int (int i) { return 0; }
int fun_int16_t (int16_t i) { return 0; }
int fun_short (short i) { return 0; }
int fun_INT16_TYPE (__INT16_TYPE__ i) { return 0; }
И скомпилируйте с помощью -Os -S
или -Os -save-temps=obj
. Созданная сборка затем отображает следующие искаженные имена:
> grep ^_Z foo.s
_Z7fun_inti:
_Z11fun_int16_ti:
_Z9fun_shorts:
_Z14fun_INT16_TYPEi:
Обратите внимание на последнюю букву: i
означает int
, за исключением версии short
, где она есть s
.
Это означает, что у вас может быть полиморфизм между int
и short
, но не среди любого из типов i
:
int ffun (int i) { return i; }
short ffun (short i) { return i; } // OK
int ffun (int16_t i) { return i; } // error: 'int ff(int)' previously defined
Пожалуйста, предоставьте минимально воспроизводимый пример. Вопрос о привязке ссылки не требует 20 дополнительных строк некомпилируемого кода.