Passing enums as references to non-enum functions

Hey all,

I've got a library for generic interface with I2C devices (read/write) and another library that acts as a driver for a specific device. I'm trying to do two somewhat independent things here:

  • Format my I2C read/write functions to act on reference inputs and return Boolean success or failure flags. These flags are used for error handling.
  • Use enums in my driver code to avoid magic numbers.

When I try to read a device register into a variable declared as one of the enum types, I get several flavors of errors about the inability to convert between enums and other types. I've think been able to make it work with reinterpret_cast but I've gotten the impression that this should be avoided when possible.

In general, it feels like there should be a better way to do this so I'm looking for any suggestions on how to do this without reinterpret_cast while still meeting the goals.


// register.h
#include <Wire.h>

class REGISTER 
{
  const uint8_t devAdr;
  const uint8_t regAdr;
  const uint8_t size = 1;

  public:
  REGISTER(const uint8_t dev, const uint8_t reg) : devAdr(dev), regAdr(reg) {}

  bool read(uint8_t& input_ref) 
  {
    Wire.beginTransmission(devAdr);
    Wire.write(regAdr);
    if(Wire.endTransmission() != 0) { return false; }
  
    Wire.requestFrom(devAdr, size);
    input_ref = Wire.read();
    return true;
  }
};
// device.h
#include "register.h"

class DEVICE
{
  const uint8_t deviceAddress;
  REGISTER gain_reg {deviceAddress, 0x70};

  public:
  enum GainLevel
  {
    GAIN1   = 0b00,
    GAIN2   = 0b01
  };
  GainLevel currentGain = GAIN1;

  bool updateGainLevel()
  {
    return gain_reg.read(currentGain); // Error
    // return gain_reg.read(reinterpret_cast<uint8_t&>(currentGain)); //No error
  }
  DEVICE(const uint8_t dev) : deviceAddress(dev) {}
};
#include "device.h"

void setup()
{
  Serial.begin(112500);
  DEVICE device(0x10);
  
  Serial.print("Update flag: ");
  Serial.println(device.updateGainLevel());
  Serial.print("Current Gain: ");
  Serial.println(device.currentGain);
}

void loop(){}

Error as shown: error: cannot bind non-const lvalue reference of type 'uint8_t&' {aka 'unsigned char&'} to an rvalue of type 'uint8_t' {aka 'unsigned char'}

It should be

because each enum creates its own type in C++

if the parameter is an enum then use that type

bool read(GainLevel& input_ref)

and if you need to modify input_ref, use also a Gain value since it's the type

    input_ref = GAIN2;

PS: that seems weird

I've updated my example code to be a bit more clear about the delineation between files and classes.

@b707 @J-M-L The register library is device agnostic so I can't / don't want to add an overload for this specific enum

Use a temp uint8_t variable:

 bool updateGainLevel()
  {
    uint8_t tmp;
    if (gain_reg.read(tmp))  {currentGain = GainLevel(tmp); return true;}
    else return false;
   }

in a future posts, please do not update a code in the old messages, post every new code redaction in a new message

well an enum is a formal type in C++.

It can always be promoted into its integral value but the opposite is not true and so if a function expects a reference to an uint8_t then you. need to pass an uint8_t type

What do you expect to be printed given that device.updateGainLevel() returns a bool?

0 or 1 ?
(Print does not support bool so the compiler will select take the int promotion signature)

Not sure this is the best term but this feels like a "brute force" method of solving this problem. I'm aware I can get around the issue by just making new variables but that's what I'm trying to avoid.

Yeah just a 1 or 0 here. The purpose of that line is just a illustration of using the bool output for something. In the real program its used for error handling.