Pointer to array Maintain counter of elements

207 Views Asked by At

I have an interface which multiple classes inheritance.

    class someInterface
    {
        virtual void someMethod() = 0;
    }
    class A : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

    class B : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

    class C : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

For each of the classes A, B, C i have created an array with different sizes of their actual type inside a container class.

    class AContainer 
    {
        public:
            A As[10];
    }

    class BContainer 
    {
        public:
            B Bs[5];
    }
    etc...

Furthermore i have an array of pointers to "SomeInterface", where i want to have a pointer to each of the actual arrays like this.

    #define SOMEINTERRFACE_SIZE 3
    someInterface *array[SOMEINTERRFACE_SIZE];
    array[0] = AContainer.As; //Could also just be &AContainer.As[0]
    array[1] = BContainer.Bs;
    array[2] = CContainer.Cs;

    for (int i = 0; i < SOMEINTERRFACE_SIZE; ++i)
    {
        int elements = //Here i need a solution to get the size
                       //So i can iterate through the array, which the pointer points to.
        for (int i = 0; i < elements; ++i)
        {
            //Call the interface method on each element.
        }
    }

The problem occurs, when i have to use the someInterface array, since it isn't possible to get the size of the actual array through the someInterface pointer..

What is a good solution to this problem? I really need some help to solve this. Also don't want to use dynamic allocation, so no solution with vector<> or malloc etc. because I'm writing to an Arduino.

3

There are 3 best solutions below

4
On BEST ANSWER

It won't work. In C++ you have to know the size of the elements in an array. A, B, and C might be different sizes, so you can't treat arrays of them the same.

&AContainer.As[i] == &AContainer.As + i * sizeof(A)

but

&BContainer.Bs[i] == &BContainer.Bs + i * sizeof(B)

So it's impossible for the same machine code to iterate over arrays of A and of B. If you want to iterate over an array of objects, you need to know the exact type.

Remember in C++, if you want to get a polymorphic virtual call, you need to go through pointer or reference. The solution is to copy pointers to the elements in each array into one "master" array.

SomeInterface *ptrs[NUM_A + NUM_B + NUM_C];
SomeInterface **dest = ptrs;
for (int i = 0; i < NUM_A; ++i) {
    *dest++ = &AContainer.As[i];
}
for (int i = 0; i < NUM_B; ++i) {
    *dest++ = &BContainer.Bs[i];
}
// et cetera...

This only uses a little bit of extra space because you're storing pointers, not actual objects.

EDIT: I guess you could do something like this if you really want to save the space:

someInterface *arrays[] = { AContainer.As, BContainer.Bs, CContainer.Cs };
int objSizes[] = { sizeof(A), sizeof(B), sizeof(C) };
int arrLengths[] = { NUM_A, NUM_B, NUM_C };

for (int j = 0; j < sizeof(arrays)/sizeof(arrays[0]); ++j)
{
    void *ptr = arrays[j];
    for (int i = 0; i < arrLengths[j]; ++i) {
        someInterface *iptr = (someInterface *)ptr;
        iptr->method();
        ptr += objSizes[j];
    }
}

(this is untested, you might need to tweak a little.)

In theory since all those arrays are full of compile-time constants, it should optimize out to something fast. If it doesn't, the code will run slower because it will be incrementing pointers by a value only known at runtime instead of compile time. You should check the assembly output if you really care about speed.

0
On

Difficult to answer without knowing more details of your application - but here a a few ideas that might help.

Given:

class someInterface { public: virtual char someMethod() = 0; };

class A : public someInterface { public: char someMethod() { return 'A'; } };
class B : public someInterface { public: char someMethod() { return 'B'; } };
class C : public someInterface { public: char someMethod() { return 'C'; } };

You could hand-roll something like this:

class Array {
public:
    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < countA  ;  ++i) function(As[i]);
        for (size_t i = 0  ;  i < countB  ;  ++i) function(Bs[i]);
        for (size_t i = 0  ;  i < countC  ;  ++i) function(Cs[i]);
    }
private:
    enum {countA = 10, countB = 5, countC = 3};
    A As[countA];
    B Bs[countB];
    C Cs[countC];
};

void doSomeMethod(someInterface& element) {
    std::cout << element.someMethod();
}

int main(int, char**) {
    Array array;
    array.forEach(doSomeMethod);
    return 0;
}

Note that by using the "callback" function doSomeMethod, we get around a typical problem of dispatching in polymorphic collections. Of course, you don't want to keep hand-rolling things like that. Fortuanately the Arduino C++ compiler I checked out has template support, so you could do something like:

template <class T, size_t _size, class NextArray = void>
struct Array {
public:
    typedef T value_type;
    enum {size = _size};

    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < _size  ;  ++i)
            function(elements[i]);
        nextArray.forEach(function);
    }
private:
    T elements[_size];
    NextArray nextArray;
};

template <class T, size_t _size>
struct Array<T, _size, void> {
public:
    typedef T value_type;
    enum {size = _size};

    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < _size  ;  ++i)
            function(elements[i]);
    }
private:
    T elements[_size];
};

void doSomeMethod(someInterface& element) {
    std::cout << element.someMethod();
}

int main(int, char**) {
    Array<A, 10, Array<B, 5, Array<C, 3> > > array;
    array.forEach(doSomeMethod);
    return 0;
}

Which gets the compiler to write it for you for different combinations of types and sizes. A few things worth noting:

  1. All the magic is done at compile time. Check out the assembly generated by an optimising compiler to see how small and fast this can be.
  2. Read up on c++ "functors" if your callback function needs some state.
  3. If your compiler supports variadic templates and/or lambdas, this becomes simpler (I'm assuming the Arduido compiler doesn't yet)

If you can't use the callback approach (and your compiler doesn't support lambdas yet), then you could try this next option, which incurs some small run-time cost over the option given above:

template <class Interface>
class ArrayInterface {
public:
    virtual size_t getSize() = 0;
    virtual Interface& getElement(size_t index) = 0;
};

template <class T, class Interface, size_t size>
class Array : public ArrayInterface<Interface> {
public:
    size_t getSize() { return size; }
    Interface& getElement(size_t index) { return element[index]; }
private:
    T element[size];
};

int main(int, char**) {
    Array<A, SomeInterface, 10> As;
    Array<B, SomeInterface, 5> Bs;
    Array<C, SomeInterface, 3> Cs;

    const int SOMEINTERRFACE_SIZE = 3;
    ArrayInterface<SomeInterface>* array[SOMEINTERRFACE_SIZE] = {&As, &Bs, &Cs};

    for (size_t i = 0  ;  i < SOMEINTERRFACE_SIZE  ;  ++i) {
        ArrayInterface<SomeInterface>& innerArray = *array[i];
        for (size_t j = 0  ;  j < innerArray.getSize()  ;  ++j)
            std::cout << innerArray.getElement(j).someMethod();
    }

    return 0;
}

(This last one uses an outer array of pointers, as per your question)

This post is already too long, so I haven't gone into much detail, or delved into options such as a single, flat array of member function pointers. If you have any questions, just shout.

1
On

Is it what you are trying to achive? Iterate over a list of objects and call a reimplemented method of a common interface?

Put this piece of code anywhere in global C++ scope for testing.

#include <vector>
#include <iostream>
int TestSomewhereInCppGlobalScopeCode()
{
    class someInterface
    {
        public:
            virtual void someMethod() = 0;
    };

    class A : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "A::someMethod()";
            }
    };

    class B : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "B::someMethod()";
            }
    };

    class C : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "C::someMethod()";
            }
    };

    std::vector<someInterface*> ListOfObjectsHavingCommonInterface;

    ListOfObjectsHavingCommonInterface.push_back( new A );
    ListOfObjectsHavingCommonInterface.push_back( new B );
    ListOfObjectsHavingCommonInterface.push_back( new C );

    for ( std::vector<someInterface*>::iterator it = ListOfObjectsHavingCommonInterface.begin();
          it != ListOfObjectsHavingCommonInterface.end();
          ++it )
    {
        (*it)->someMethod();
    }

    return 0;
}

static int TestSomewhereInCppGlobalScopeCode_Caller = TestSomewhereInCppGlobalScopeCode();