I'm running into the following error depending on which type I choose for a variable.
/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);
^~~~~~~~~~~~~~~
Here is the body of 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);
}
The problem happens when new
ing 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) {}
...
^^ As you can see, _primitive_value
is a reference.
Oddly, this works when I pass an actual int
or int16_t
. However, I run into this error whenever I pass in a value that is equivalent but must be cast into a int16_t
, like short
.
SUCCESS (int16_t
): (compiles without warnings)
int16_t potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
SUCCESS (int
): (compiles without warnings)
int potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
FAIL (short
): (error shown above)
short potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
EDIT: Originally, I forgot to mention that this particular example is compiling for a 16-bit platform. So
int
,short
andint16_t
should all be the same size.
In a really bizarre twist, this works when I pass a short
or int16_t
. However, I run into a similar error whenever I pass in an int
value.
SUCCESS (int16_t
): (compiles without warnings)
int16_t potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
SUCCESS (short
): (compiles without warnings)
short potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
FAIL (int
): (similar error as shown above)
int potentiometer;
...
void initProperties() {
...
ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
Can someone explain what exactly is happening here with the type conversion?
I'm looking for a POSIX compliant way for to the compiler to treat them equivalently, or a way to enhance the code to allow this to work. Any solution will need to compile for multiple architectures (16-bit, 32-bit, etc.), as well as work on multiple toolchains (avr-gcc
, arm-none-eabi-g++
, etc.).
NOTE: I don't have control over the code base; it is a large open-source project. I really need a surgical solution, because the more broad the refactor work, the harder it will be to get the PR accepted.
NOTE: This answer welcomes improvement, and is left solely as a breadcrumb to others. It is based-on empirical observation, and not digging into the source code of the compilers in question or researching the ISO.
It appears that the avr-gcc
compiler aliases int16_t
as int
(or vice-versa), and short
is defined separately. While the arm-none-eabi-gcc
compiler aliases int16_t
as short
(or vice-versa).
The successes can be attributed to the fact that they are, in fact, the same data type so no casting or conversion is required. As such, the failures are stemming from the fact a conversion or cast is deemed necessary by the compiler.
Once a cast has occurred, a temporary is created. Obviously, a temporary can not be bound to a non-const reference (which makes sense of the error message), because the reference would not have storage associated with it and therefore could not be modified during runtime.
If you are struggling to conceptualize this, then think of a cast from an integer to a float. It would be impossible to update an underlying integer by modifying a float value.
In my case, the solution I needed was to further template my integer-based classes and functions. This ultimately abstracted all the integer operations behind a single generic implementation.