-1

I am working on a project with an ESP32 and a MCP23017.

In the main program I create an object for the MCP and initialize the address.

Adafruit_MCP23X17 pin_Expander;
const int expander_addr = 0x20; // Adress 0x20 

void setup()
{
    Serial.begin(9600);

    pin_Expander.begin_I2C(expander_addr);
    expander_Init(pin_Expander);
}

Since I use many IOs in the project, I wanted to outsource the initialization of these and wrote a function. I have created a separate header file for the function.

void expander_Init(Adafruit_MCP23X17 pin_Expander)
{
    // Load the GPIOs
    pin_Expander.pinMode(pin1_Motor1, OUTPUT); // I have removed the other pins

    // Set all GPIOs to LOW
    pin_Expander.digitalWrite(pin1_Motor1, LOW); // I have removed the other pins
}

I wonder if this is a legitimate way to pass an object into another function?

wohlstad
  • 12,661
  • 10
  • 26
  • 39
Teebow
  • 41
  • 7
  • 5
    You're passing by value, so that's probably incorrect. – ChrisMM Jan 04 '23 at 17:11
  • You need `expander_Init` to accept by reference (`Adafruit_MCP23X17 & pin_Expander`) if you want it to affect the instance passed to it from `main`. – wohlstad Jan 04 '23 at 17:16
  • If correct or not actually depends on the Adafruit object. Passing by value means *copying* the object! This *can* be just fine (small object not to be modified anyway or if modifying within the function is fine while the original object *shall* remain unmodified), this can be a costly operation but *otherwise* fine (if the object is large or requires dynamic memory allocation, otherwise as before) and this can go totally wrong (if the original object needs to change its state in any way) – unfortunately the latter being the most common case... – Aconcagua Jan 04 '23 at 17:18

1 Answers1

1

Adafruit_MCP23X17 is a C++ class, not some typedef that simulates reference semantics in C, so when you declare your function:

void expander_Init(Adafruit_MCP23X17 pin_Expander)

you're accepting the object by value, not by reference, which means expander_Init(pin_Expander); copies pin_Expander and passes in the copy. Any mutations to the copy would not typically affect the original version (unless the class is poorly designed†).

If you want to modify the original version, change the prototype to:

void expander_Init(Adafruit_MCP23X17& pin_Expander)
                                 // ^ added & to make it accept by reference

which means you'll accept a reference to whatever the caller passed, no copies involved, and anything you do to that reference is equivalent to doing it to the original caller-passed object.


† I'll note: I wouldn't be surprised if the class was poorly designed, and all instance methods effectively modified global, not per-instance, state (low-level hardware manipulation can often make assumptions like this), so your original code might work just fine, but it's not guaranteed without source code inspection, and it might involve some inefficiencies in the extra copy-and-destroy work that passing-by-reference avoids.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271