Reputation: 1211
In working on Kira3, I was playing around with the C++ compiler and looking for a good way of implement Kira's duck typing. I was hoping (as it has been a few years of direct C++ programming) that I could use multiple inheritance for member access under multiple types. Alas, I have failed so far...
The ideal code would look like:
class WithX { public: int x; };
class WithY { public: int y; };
class WithZ { public: int z; };
class Point2D : public WithX, public WithY { };
class Point3D : public WithZ, public WithX, public WithY { };
void ZeroOut(Point2D * p) { p->x = 0; p->y = 0; };
int _tmain(int argc, _TCHAR* argv[])
{
Point3D* p = new Point3D();
p->x = 1;
p->y = 1;
p->z = 1;
ZeroOut(p);
return 0;
}
However, it throws a typing error at the invocation of ZeroOut(p). This is ultra sad face. I can force it work by creating a type tower. In the above example, I could change class Point3D : public WithZ, public WithX, public WithY { }; to class Point3D : public Point2D, public WithZ { };
The problem now is when I have structures that overlap. I would either throw an error that would be a pain to solve in kira code, or I have the compiler do something different. I tried
class Point3D : public Point2D, public WithZ, public WithX, public WithY { };
with the hope of the compiler combining them, but this gives ambiguous access to member variables. This can be fixed by replicating writes to the ambiguous member variables, and this could be a solution during my compilation phase. This solution requires more memory.
Any ideas how to solve this without a memory loss?
Or, is there a way to cast variables to multiple types? like
(WithX,WithY *)p
Upvotes: 2
Views: 554
Reputation: 258398
I don't know anything about Kira, but you could solve at least this particular issue with a template function -- which is kind of like compile-time duck typing, in a way.
#include <iostream>
class WithX { public: int x; };
class WithY { public: int y; };
class WithZ { public: int z; };
class Point2D : public WithX, public WithY { };
class Point3D : public WithZ, public WithX, public WithY { };
// As long as we pass in a type that has an x and a y member,
// ZeroOut will compile and run correctly, setting x and y to zero.
template <typename T>
void ZeroOut(T * p) { p->x = 0; p->y = 0; };
int main(int argc, char* argv[])
{
Point3D* p = new Point3D();
p->x = 1;
p->y = 1;
p->z = 1;
ZeroOut(p);
std::cout << p->x << " " << p->y << " " << p->z << std::endl;
return 0;
}
correctly outputting:
$ ./a.out 0 0 1
You mentioned in a comment that is uses more code -- I'd like to point out that the added code is negligible. I changed the main function to:
int main(int argc, char* argv[])
{
Point3D* p = new Point3D();
p->x = 1;
p->y = 1;
p->z = 1;
ZeroOut(p);
#ifdef USE_2D
Point2D *q = new Point2D();
q->x = 1;
q->y = 1;
ZeroOut(q);
#endif
std::cout << p->x << " " << p->y << " " << p->z << std::endl;
return 0;
}
and compiling this way gives:
$ g++ p.cpp -o just3 $ g++ p.cpp -DUSE_2D -o 3and2 $ wc -c *3* 9949 3and2 9908 just3 19857 total
Are you really going to split hairs over a 41 byte difference in the executable? Well, if you are, just turn on optimization:
$ g++ -O2 p.cpp -o just3 $ g++ -O2 p.cpp -DUSE_2D -o 3and2 $ wc -c *3* 9882 3and2 9882 just3 19764 total
Upvotes: 8
Reputation: 56123
I tried ... with the hope of the compiler combining them, but this gives ambiguous access to member variables.
To solve that problem, try virtual inheritance.
Upvotes: 2
Reputation: 8318
Overload zeroout?
void ZeroOut(Point2D * p) { p->x = 0; p->y = 0; };
void ZeroOut(Point3D * p) { /* whatever you want*/ };
Upvotes: 3