5

I am working on an embedded platform with limited capabilities, so vectors/STL are not available.

This may be a trivial problem, but I do not have much experience in C++ (only C and C#, which may make me blind to an obvious c++ way to do it).

Consider the following example:

class Parent {
};

class Child : public Parent {
};

void Test(Parent* parents, uint8_t parentCount) {
    // Accessing parent[x] is problematic when 'parents' contains a derived type
}

int main() {
    // This is OK
    Parent parents[3];
    Test(parents, 3);

    // This causes problems
    Child children[3];
    Test(children, 3);

    return 0;
}

Obviously it is problematic to iterate over parents in Test(), if a pointer to an array of derived classes is provided, because the memory footprint of Parent is assumed during the iteration.

The only solution I see is to pass an array of pointers of type Parent (Parent** parents), but that seems cumbersome. Is there some C++ mechanism I am not aware of, like passing the array as a reference or something?

Rev
  • 5,827
  • 4
  • 27
  • 51
  • 3
    Make `Test` a template function? Use array of pointers (not that I really recommend it, unless your classes are polymorphic). The solution (these or others) depends very much on use-case. – Some programmer dude Apr 28 '16 at 12:33

3 Answers3

7

You could use this approach:

template <class T>
void Test(T* parents, uint8_t parentCount) {
    // Code that accesses parent[x]
}

and then use it like this:

int main() {

    Parent parents[3];
    Test(parents, 3);

    Child children[3];
    Test(children, 3);

    return 0;
}
Rev
  • 5,827
  • 4
  • 27
  • 51
Marko Popovic
  • 3,999
  • 3
  • 22
  • 37
  • 1
    You should not specify the template type for function. You should let the compiler deduce it for you. – NathanOliver Apr 28 '16 at 12:38
  • @NathanOliver You are right. I've updated my answer. – Marko Popovic Apr 28 '16 at 12:39
  • As I pointed out, the standard template library is not available. – Rev Apr 28 '16 at 12:39
  • 2
    @Rev1.0 This does not use the standard template library. This is making your own template function. – NathanOliver Apr 28 '16 at 12:41
  • 1
    @Rev1.0 This is not from the STL, It just uses templates which are part of C++ language. – Marko Popovic Apr 28 '16 at 12:42
  • Yes that's it. It is not STL. This is the elegant way to overcome C++ native vectors problem: vectors are passed by passing pointer to the first element and it looks the same as plain pointer. – Dmitriy Zapevalov Apr 28 '16 at 12:43
  • @DmitriyZapevalov I think you mean array. A vector is not converted to a pointer. – NathanOliver Apr 28 '16 at 12:44
  • @NathanOliver you are right. For c++ array is appropriate name :) – Dmitriy Zapevalov Apr 28 '16 at 12:46
  • Ah OK, I somehow assumed that template support is not available at all. I haven't read about C++ templates yet, but from what I get its similar to c# generics and the STL is equivalent to the C# libraries that provide general purpose generic classes. I am going to do some reading now... – Rev Apr 28 '16 at 12:53
  • @MarkoPopovic: I have one issue with this solution, is there a way to specify a base type (like Parent) for the template? In C# you could do "GenericList where T : Parent" and work with that type without requiring a cast or type check within the generic function/class. – Rev Apr 28 '16 at 13:23
  • @Rev1.0 Don't let the similar syntax fool you, this is not the same as C# generics. The template mechanism creates a new instance of the function/class for every new type during compilation. It is possible that something similar to what you want could be achieved using **template metaprogramming** but I would advise not to try since you can't use STL. Implementing that from scratch requires excellent knowledge of C++ and templates. – Marko Popovic Apr 28 '16 at 13:42
  • Thanks for the info. So in the end, I either use this approach and have the trouble of type checking within the method (not sure how much trouble this really is in C++), or I go for the Parent** approach (as pointed out by user2079303) and get a clean method implementation at the cost of some overhead when calling it. – Rev Apr 29 '16 at 06:26
  • OP says "an array of derived classes is provided" which tells me a mix of different derived classes can be in the one array passed to the function. This solution doesn't support this. – ytoledano Sep 23 '18 at 19:26
  • @ytoledano To have a mix of different derived classes in an array (in C++), you need an array like this `Parent parents[length]`, and then assign different derived classes to the elements. But then, the original version of the method (which takes pointer to Parent) can be used. I think the biggest problem is that you guys want to use C++ templates to have all the features of C# generics. – Marko Popovic Sep 25 '18 at 10:04
3

If template is not an option and when the user of Test can not depend on Child and can't even know it's size, then you can use an array of pointers:

void Test(Parent** parents, uint8_t parentCount);

int main() {
    Child children[n];
    Child* pointers[n];
    for(int i = 0; i < n; i++)
        pointers[i] = &children[i];
    Test(pointers);
}

Note that in this trivial example, we do know the size of the object whose pointers we pass, but in general, we may not be able to make that assumption for all users of Test.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • This approach was my initial one, to quote myself: "The only solution I see is to pass an array of pointers of type Parent (Parent** parents), but that seems cumbersome." But thanks for verifying this though. – Rev Apr 28 '16 at 13:04
  • @Rev1.0 yeah, I noticed. This is here for the people, who are looking for a solution and to whom the other answers aren't an option. – eerorika Apr 28 '16 at 13:07
  • 1
    This is the correct solution. A solution which passes a template array doesn't work for arrays that have a mix of different derived classes. If you could use STL you'd use a vector of unique_ptr or shared_ptr. – ytoledano Sep 23 '18 at 19:22
1

If you can't use templates, you can do this:

class Parent {
};

class Child : public Parent {
};

void Test(Parent* parents, uint8_t parentCount, uint16_t parentSize) {
    for (uint8_t ii = 0; ii < parentCount; ++ii) {
        void* parentvoid = reinterpret_cast<char*>(parents) + ii * parentSize;
        Parent* parent = parentvoid;
    }
}

int main() {
    Parent parents[3];
    Test(parents, 3, sizeof(parents[0]));

    Child children[3];
    Test(children, 3, sizeof(children[0]));
}
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Interesting but somewhat messy approach. I hoped C++ would makes things cleaner ;) I guess I would prefer the "array of pointer" approach in comparison to this. Thanks for feedback. – Rev Apr 28 '16 at 13:00
  • @Rev1.0: Well, the "array of pointers" approach is less efficient, and I figured you wouldn't want that since you're apparently on a super-constrained system, but it is certainly workable. – John Zwinck Apr 28 '16 at 13:02