0

I have created a class in C++. Each object corresponds with an I/O pin on a microcontroller. I can create an array of the objects and then set each pin to an output with a for loop. I would like to be able to set multiple pins to an output at the exact same time. This can be done with the hardware.

I am trying to create a method that works on an array of objects. You would use it like this:

Pin myPins[] = {0,1,2};
myPins.setOuput();

Basically I want to create a method that works on an array of objects. Is it possible to create a setOuput method like this? If yes, how? Thanks.

Update 1

New non-member method

void setOutput(Pin pins[], int size) {
    volatile uint8_t* _DDR = pins[0].getDDR();
    uint8_t _offset = 0;
    for (int i = 0; i < size; i++) {
        _offset |= pins[i].getOffset();
    }
    DDR_HIGH;
}

I am using the _ in the names so that my existing macro works. Not great but less code.

Alec Fenichel
  • 1,257
  • 1
  • 13
  • 27
  • There's nothing wrong with simply having a non-member function that handles that (ex: `setOutput(myPins)`) If you need to access private members, you can make it static inside the Pin class. – KABoissonneault Jul 10 '15 at 17:12

2 Answers2

2

Nope, you cannot add a method to a classic array the way you intend to. However, you could create a class that inherits from, say, a std::vector<Pin>, and add methods to it, like this:

class Pins : public std::vector<Pin>
{
public:
    void setOutput() { /* your code here */ }
};

That way, using C++11's uniform initialization, you could use a similar syntax:

Pins myPins = {0, 1, 2};
myPins.setOutput();

Edit: as per the comments, subclass a STL container is a quick and dirty solution and not the best idea. You could, however, create your own wrapper class very simply:

class Pins
{
    std::vector<Pin> mPins;

public:
    Pins (std::initializer_list<Pin> pins) : mPins(pins)
    { }

    void setOutput()
    {
        cout << "Pins in this pinset:" << endl;

        for (Pin & p : mPins)
        {
            cout << "\t" << p << endl;
        }
    }
};

That works exactly the same:

Pins myPins = {0, 1, 2};
myPins.setOutput();
José Tomás Tocino
  • 9,873
  • 5
  • 44
  • 78
  • Deriving a class which is not meant to be used as a base class is dangerous, though. `Pins`' destructor should never be non-trivial – KABoissonneault Jul 10 '15 at 17:09
  • http://stackoverflow.com/q/6806173/1116364 I'd advice against subclassing here. This leads to a very rigid design. – Daniel Jour Jul 10 '15 at 17:14
  • Inside the setOuput function for the Pins class, how to I access each Pin? I need to call a function that returns the offset value for each Pin in the vector. Then I bitwise or them all together. In my Pins class I don't have a way to destroy an object. Because it is for use on a microcontroller it isn't really needed. I don't understand the harm of inheriting from vector? – Alec Fenichel Jul 10 '15 at 18:14
  • It's the quick and dirty way of doing it. I've edited the answer to provide an alternative class that does not subclass `std::vector`. In this new class you can access the pins using `mPins[i]`. – José Tomás Tocino Jul 10 '15 at 21:35
0

Most probably your setOutput member function is reading some possibly multi byte value, changing a bit depending on the pin, and writing it back.

C++ arrays can't have member functions.

To achieve a similar effect, you should split the work your original setOutput is doing:

  1. Read some hardware config
  2. Do the bit twiddling
  3. Apply the changes

Then you can let multiple pins do their bit twiddling before applying the final output.

Example:

Pin::Config cfg = Pin::read_config();
// the following could also happen in a loop over an array.
cfg = pin1.enableOutput(cfg);
cfg = pin4.enableOutput(cfg);
// or pass by reference and mutate, if you
// want a less functional style
// e.g. pinN.enableOutput(cfg)!
Pin::write_config(cfg);

This way you still have good encapsulation, but better control. Then you can write a free function to operate on arrays, vectors or whatever collection of pins, if needed:

template<typename It>
void setOutputPins(It start, It end) {
  Pin::Config cfg = Pin::read_config();
  for (; start != end; ++start) {
    cfg = (*start).enableOutput(cfg);
  }
  Pin::write_config(cfg);
};

Using it with C arrays:

Pin array[5]; // or a pointer for dynamic arrays
// ...
setOutputPins(array, array + size);

Don't make everything OO. It'll make your life harder.


[...] a function that returns the offset value for each Pin [...]. Then I bitwise or them all together.

So you don't even need that reading step. And since you bitwise or them, you could even do something like this:

Pin::write_config(pin1.output_mask() | pin4.output_mask());

You can make the function generic, too: Or pass a member function pointer:

template<typename It>
void setPins(It start, It end, Pin::Config (Pin::*fn)(void)) {
  Pin::Config cfg = 0; // identity for bitwise or
  for (; start != end; ++start) {
    cfg |= ((*start).*fn)();
  }
  Pin::write_config(cfg);
};

And the pass a pointer to the member function you want to invoke:

setPins(array, array + size, &Pin::asInput);

Example here.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • I understand what you are saying and I was originally thinking of just creating a free function where you input an array of Pins and it loops through the array and gets the offset for each Pin (I have a method for getting the offset) and bitwise ors them together and then sets the register with the combined offset. I think the OO approach looks cleaner to use but it might not be worth it. – Alec Fenichel Jul 10 '15 at 18:22
  • Here is the source: https://github.com/fenichelar/Pin Because I am going to write a bunch of these free methods (setInput, setOuput, setHigh, setLow, ...) I wanted to try and keep them grouped in a class. – Alec Fenichel Jul 10 '15 at 18:26
  • Except for the toggle versions (which require state) the method from my latest edit should work – Daniel Jour Jul 10 '15 at 18:50
  • I don't think the toggle is necessary as you wouldn't likely need to toggle multiple pins simultaneously. I updated my questions with the new code for setOutput. Is there a better way of doing this than writing these blocks for every method? – Alec Fenichel Jul 10 '15 at 19:07
  • You only need the last function. Then you pass a pointer to the member function that you want. – Daniel Jour Jul 10 '15 at 19:56
  • How? The member function uses the offset stored internally in the object. – Alec Fenichel Jul 10 '15 at 20:08
  • Isn't that offset like the "mask" in my example code? – Daniel Jour Jul 10 '15 at 20:25