0
votes

I have a class that takes a "list" of pins as argument in the constructor. Right now, this "list" is just a const array (const unsigned int (&pins)[N]). I'd like to overload the constructor in such a way that you can use an initializer_list as well as a (const) array.
I can get it working with separate classes, but I'm unable to combine them.

The basic structure of my test class:

#include <iostream>
#include <cstring>
using namespace std;

class X
{
  public:
    X(... pins);
    ~X();
    void print() {
        for (unsigned int i = 0; i < length; i++)
            cout << pins[i] << ' ';
    }

  private:
    (const) unsigned int *pins;
    const size_t length;
};

Class A (using std::initializer_list)

  public:   
    A(initializer_list<unsigned int> pins)
        : length(pins.size()) {
        this->pins = (unsigned int *)malloc(sizeof(unsigned int) * pins.size());
        memcpy(this->pins, pins.begin(), sizeof(unsigned int) * pins.size());
    }
    ~A() {
        free(pins);
    }
  private:
    unsigned int *pins;

Class B (using an array)

  public:
    template <size_t N>
    B(unsigned int (&pins)[N])
        : length(N) {
        this->pins = pins;
    }
  private:
    unsigned int *pins;

Class C (using a const array)

  public:
    template <size_t N>
    C(const unsigned int (&pins)[N])
        : length(N) {
        this->pins = pins;
    }
  private:
    const unsigned int *pins;

This allows me to do things like:

A a({1, 2, 3});

unsigned int pb[] = {4, 5, 6};
B b(pb);

const unsigned int pc[] = {7, 8, 9};
C c(pc);

a.print();
b.print();
c.print();

And this prints 1 2 3 4 5 6 7 8 9 as expected.

How can I combine these different constructors in one class? I want to be able to initialize an object in either of the three ways shown above.

If I make pins a const, I can't do things like memcpy(pins, ...), and if I don't make it a const, I can't use a const array as argument for the constructor.

(The target platform is Arduino, so most fancy and memory-intensive C++11 tricks are not supported.)

1
B and C can be combined I think if you assign it through the initializer instead of inside the constructor's function, so length(N), pins(pins). const members can only be set through the initializer, not inside the constructor. - be aware you are storing a pointer though. otherwise there is always the option to use the mutable keyword that will allow setting it, just make sure you keep it private so not jsut anything can mess with it after constructingGiel
So wait - you want pins to be an owning pointer to heap-allocated memory sometimes, but a non-owning pointer to externally provided array other times? How would the destructor know whether it's supposed to call free(pins)?Igor Tandetnik
Yes. But I'm open to better suggestions. I just need a list of pins, and I'd like to initialize it inline like A a({1, 2, 3}); as well as using arrays.tttapa

1 Answers

2
votes

Something along these lines, perhaps:

class X
{
  public:
    X(std::initializer_list<unsigned int> aPins)
        : storage(aPins), length(aPins.size()) {
      pins = length ? &storage[0] : nullptr;
    }

    template <size_t N>
    X(unsigned int (&aPins)[N])
        : length(N) {
        pins = aPins;
    }

    template <size_t N>
    X(const unsigned int (&aPins)[N])
        : length(N) {
        pins = aPins;
    }

    void print() {
        for (unsigned int i = 0; i < length; i++)
            std::cout << pins[i] << ' ';
    }

  private:
    const unsigned int* pins;
    const size_t length;
    std::vector<unsigned int> storage;
};

Demo