0

I have a structure containing lots (like, hundreds) of pointers. Each pointer is a different type, but they all inherit from a common base class --- let's call it Base. I'm using multiple inheritance. (This is all machine-generated, which is why it's weird.)

e.g.:

class Data {
    Class1* p1;
    Class2* p2;
    Class3* p3;
    ...etc...
};

I want to call a method defined in Base on all of these. I can generate code like this:

void callFooOnData(Data* p)
{
    if (p1) p1->Foo();
    if (p2) p2->Foo();
    if (p3) p3->Foo();
    ...etc...
}

The problem is, I've got lots and lots of these, of a billion different kinds of Data, and the above code ends up being very large and is affecting my footprint. So I'm trying to replace this with something smarter.

In C, I could simply take the address of the first structure member and the count, and do something like:

void callFooOnData(Data* p)
{
    callFooOnObjects(&p1, 3);
}

But, of course, I can't do this in C++, because the structure members aren't a uniform type, and casting them to Base* may involve changing them, and this may involve changing the pointer, and because they're not a uniform type the pointers will have to be changed differently for each member, and I can't do that.

Is there a way to do something like this is C++? This is all machine generated code, so it doesn't have to be pretty, thankfully --- that shark has already been jumped --- but I do need it to be as portable as possible...

(I do have access to RTTI, but would like to avoid it if possible for performance reasons.)

Update:

So, as far as I can tell... you just can't do this in C++. Simply can't be done. I was misled by the fact that it's totally straightforward in C. In C++ you can only safely cast a pointer between two types in a class hierarchy if you have a well typed pointer to begin with, and of course, I don't.

So I'm going to have to change the underlying problem to avoid this: the solution I've come up with is to store every pointer in the structure twice, once in an array of Base* for iterating over, and once as a ClassWhatever* for calling methods on. Which sucks, of course, because it's going to double the size of the Data structure.

So if anyone would like to confirm this (I would love to be proven wrong), I will happily mark their answer as correct...

David Given
  • 13,277
  • 9
  • 76
  • 123

6 Answers6

4

Each pointer is a different type, but they all inherit from a common base class --- let's call it Base

Instead of having hundreds of members, just keep a container of Base class pointers:

class Data {
   std::vector<Base*> objects;
};

In a good design, you don't really need to know the type of each object, and it's actually better if it's abstracted away. Remember, it's always good to program against interfaces, not concrete classes.

and casting them to Base* may involve changing them

Not really, for public inheritance the cast should be implicit.

If you only call Foo() on all of them, Foo() can be a virtual method in the base class, that way you take full advantage of polymorphism.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • He said the class is machine generated, so maybe he can't make a vector? – loganfsmyth Jul 08 '12 at 18:28
  • @loganfsmyth I missed that part... but if it's machine generated, I don't see the point of the question. I mean... the code is what it is. – Luchian Grigore Jul 08 '12 at 18:32
  • Well, yes, but if I didn't need each member to be a different type I wouldn't have made them different types! It's only this one piece of code which needs to deal with them in aggregate. I am in fact contemplating storing each pointer twice: once as the real type for use in the bulk of the code, and once as a Base for use in aggregate. But that doubles the size of the Data structure as well as requiring every pointer to be written twice, so I'd rather avoid that. – David Given Jul 08 '12 at 19:05
0

But, of course, I can't do this in C++, because the structure members aren't a uniform type, and casting them to Base* may involve changing them, and this may involve changing the pointer, and because they're not a uniform type the pointers will have to be changed differently for each member, and I can't do that.

Although its a bad idea, this is not true: you can iterate over the pointers using pointer arithmetic like you do in C. The pointers are all the same size, and they have a common base class (not considering structure alignment problems etc). Its dirty but technically it works. That would be different if you hold the instances themselves as members in the class, not pointers to them.

The best way in C++11 would be to use

std::vector< std::unique_ptr<Base> > objects;

see http://www.drdobbs.com/cpp/c11-uniqueptr/240002708

RED SOFT ADAIR
  • 12,032
  • 10
  • 54
  • 92
  • This only works in the linear inheritance case, where you know that pointers of different types to the same object have the same value. When using multiple inheritance, like I am, this isn't true --- when casting a Class1* to a Base*, the compiler inserts instructions to change the pointer. – David Given Jul 08 '12 at 19:54
0

You must "fight" with auto-generated code by auto-generating your code too. You can use tools like ctags to "parse" your auto-generated classes and auto-generate your code from ctags output. See http://ctags.sourceforge.net/ctags.html.

You can also try to cast your Data to tuple as advised in this, possible duplicate, question: Iterate through struct variables.

I am not sure which is faster...

If you can modify tool which auto-generates this source code - maybe best would be to extend this tool...

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
0

Another option (not compileable):

class DataManipulator 
{
public:
   DataManipulator(const Data& aData_in)
   {
      objects.add(aData_in->Class1);
        .
        .
   }
   void operate()
   {
       for(objects_iterator...)
       {
           if(*objects_iterator != NULL)
                 objects_iterator->Foo();
       }
   }
private:

   std::vector<Base*> objects;
};
PermanentGuest
  • 5,213
  • 2
  • 27
  • 36
0

I did not want to change my previous answer. However I come to another solutions which should work for you: full example here: http://ideone.com/u22FO

The main part below:

struct C {
  A1* p1;
  A2* p2;
  A3* p3;
  A4* p4;
  // ...
};

template <class D, size_t  startNumber, size_t numMembers, bool notFinished>
struct FooCaller;
template <class D, size_t  startNumber>
struct FooSingleCall;

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, false> {
   void operator() (D& d) {}
};

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, true> {
   void operator() (D& d) {
       FooSingleCall<D,startNumber>()(d);
       FooCaller<D, startNumber + 1, numMembers, startNumber < numMembers>()(d);
   }
};

#define FooSingleCallD(n) \
template <class D> \
struct FooSingleCall<D,n>{ \
   void operator() (D& d) { \
       d.p##n->foo(); \
   } \
}

FooSingleCallD(1);
FooSingleCallD(2);
FooSingleCallD(3);
FooSingleCallD(4);
// ... unfortunately repeat as many times as needed

template <class D, size_t numMembers>
void callFoo(D& d)
{
   FooCaller<D, 1, numMembers, 1 <= numMembers>()(d);
}

Aware: be smart enough not to define hundreds of FooSingleCall... See one of the answers to this famous question: https://stackoverflow.com/a/4581720/1463922 and you will see how to make 1000 instances in just 5 lines...

Also, please replace FooSingleCall with something retrieving N-pointer from your class - something like GetNPointer...

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
-1

All the pointers are the same size (all class object pointers in C++ are the same size), and in practice the compiler needs to be rather perverse in order to insert padding anywhere in this list of pointers. Anyway, the problem of possible padding is the same as in C. So you can do just the same as in C, no problem at all.

void foo( Base const* );

void bar()
{
    // ...
    foo( *(&thingy.p1 + 3) );
}

It's that easy.

That said, even with machine generated code the design sounds horrible, wrong, really really bad.

One does got something of that sort when generating vtables (where each pointer is to a function of generally different signature), but it's very rare. So, this sounds like an XY problem. Like, you’re trying to solve problem X, have come up with an ungood solution Y, and are now asking about imagined solution Y instead of the real problem X…

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Doesn't work, I'm afraid --- multiple inheritance means that a lot of the C assumptions about pointers are invalid. – David Given Jul 08 '12 at 19:56
  • I first wrote a comment that you're wrong, but then I realized that you can always *make* it not work. That's the opposite of making it work. I was stupid to answer your question, sorry. – Cheers and hth. - Alf Jul 08 '12 at 20:09