Похожие типы (int16_t и short) ведут себя по-разному во время создания экземпляра ссылки

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

/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);
}

Проблема возникает, когда newCloudWrapperInt16:

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 — это отсылка.

16-битная архитектура, компилятор avr-gcc

Как ни странно, это работает, когда я передаю настоящий 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 должны быть одинакового размера.

32-битная архитектура, компилятор Arm-none-eabi-gcc

Как ни странно, это работает, когда я передаю 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.

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

Passer By 06.03.2024 11:21

Это гигантская библиотека с открытым исходным кодом. Я не верю, что это возможно. В качестве альтернативы я предоставил код из сообщения об ошибке в его источник. Мгновенное голосование против, не пытаясь понять, не приветствуется. Я не верю, что эту проблему можно решить с помощью Годболта, иначе я бы сделал это сам. Должно быть очевидно, что это проблема «преобразования типов», и я предоставил весь код, связанный с происхождением типа и его преобразованием.

Zak 06.03.2024 11:22

@Zak Что неясно в этом сообщении об ошибке «невозможно связать неконстантную ссылку lvalue типа 'int16_t& {aka int&}' с rvalue типа 'int16_t {aka int}'??

Vlad from Moscow 06.03.2024 11:28

Путаница, возникающая из-за того, что на самом деле он может без предупреждения привязать int (или int16_t), но не short. Есть какой-то нюанс в типах, который мне не понятен. В данном примере это компиляция для 16-битной платформы.

Zak 06.03.2024 11:29

«Как ни странно, это работает, когда я передаю short или int16_t. Однако я сталкиваюсь с аналогичной ошибкой всякий раз, когда передаю значение int». -- В этом повороте нет ничего странного. Вы по-прежнему получаете ошибку, когда тип должен быть приведен к int16_t (как вы заметили для первого случая: «всякий раз, когда я передаю значение, которое [...] должно быть приведено к int16_t»). Разница в том, что в этом сценарии int16_t является псевдонимом short, тогда как в первом примере это был псевдоним int.

JaMiT 06.03.2024 11:50

«Это гигантская библиотека с открытым исходным кодом. Я не верю, что это возможно». -- Как сказал Йода: «Размер не имеет значения». Это вполне возможно. Отбросьте вещи, которые не имеют значения. В сообщении об ошибке указывается проблема как «аргумент 1», поэтому отбросьте остальные параметры. Проблема заключается в вызове функции, поэтому тело не имеет значения; отбросьте это и введите возвращаемый тип void. void addProperty(int16_t&) {}. Тогда вам просто нужны сайты вызовов, чтобы продемонстрировать три способа вызова функции. Это быстро и легко: int main() { int16_t int16_works = 1; addProperty(int16_works); }. Можете ли вы не использовать это?

JaMiT 06.03.2024 12:00

«Я не верю, что это возможно». В заголовке вашего вопроса спрашивается, почему это не компилируется short s; int16_t& r = s;. Но это даже неверно: в вашем сообщении об ошибке говорится, что вы привязываете неконстантную ссылку lvalue к rvalue. И если вы действительно не можете дать нам минимальный воспроизводимый пример , то какими именно волшебными ясновидящими, по вашему мнению, мы являемся, чтобы мы могли телепатически читать вашу кодовую базу?

Passer By 06.03.2024 12:05

На самом деле, Неконстантные ссылки на lvalue не могут быть связаны с lvalue другого типа должны быть дубликатами, но я думаю, что им не хватает деталей о том, что short и int являются разными типами, даже если они эквивалентны (то есть одинаковое количество битов). ).

JaMiT 06.03.2024 12:06

@JaMiT Вас обманули, ошибка говорит: «'int16_t& {aka int&}' для rvalue типа 'int16_t {aka int}'», которое не привязано к другому типу.

Passer By 06.03.2024 12:09

@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 06.03.2024 12:14

@JaMiT Я имел в виду сообщение об ошибке, в котором, по-видимому, говорилось, что тип в порядке, но категория значений неверна, и это моя ошибка. Судя по всему, старый gcc выдает неверные сообщения об ошибках.

Passer By 06.03.2024 12:17

Помимо того, что AVR является чрезвычайно ограниченным в ресурсах MCU, непригодным для динамического распределения (и C++...), существует множество технических причин вообще не использовать динамическое распределение: Почему мне не следует использовать динамическое распределение памяти во встроенных системах?

Lundin 06.03.2024 15:33

Полностью согласен, но, к сожалению, я не контролирую этот проект OSS. Я просто пытаюсь распутать беспорядок, который обнаружил.

Zak 06.03.2024 16:22

Я прочитал предложенный дубликат, и это заставило меня задуматься, существует ли временная переменная, созданная во время приведения (например, от short к int16_t), которая в конечном итоге разрушит механизм ссылок и создаст проблему, подобную той, которую я вижу.

Zak 06.03.2024 16:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
14
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

ПРИМЕЧАНИЕ. Этот ответ приветствуется для улучшения и остается исключительно в качестве основы для других. Он основан на эмпирических наблюдениях, а не на копании исходного кода рассматриваемых компиляторов или исследовании ISO.

Похоже, что псевдонимы компилятора avr-gccint16_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

Другие вопросы по теме