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.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.