I've been working on an alternative method to virtual pointers to achieving runtime polymorphism, with increased space efficiency being the primary concern. While this was simple enough, I found that when inlining was brought into consideration, my method was unable to benefit fully due to functions containing switch statements being refused inlining. While this doesn't amount to much of a speed difference in this simple test, I sought to improve upon it and came up with a method involving what I believe is called 'macro magic', which incidentally happens to offer a noticable speed advantage over virtual pointers in many cases.
What I'm asking for is info on any flaws which could limit its use.
Code:#include <iostream> #include <time> using namespace std; // This next bit is the macro that serves as a 'solution' to the non-inlining function(s?) containing switches. #define FUNC(ob) { \ switch(ob->get_type()) { \ case 1: \ ob->m1::koo(); \ break; \ case 2: \ reinterpret_cast <m2 *> (ob)->m2::koo(); \ break; \ } \ } // This determines the version of program compiled. Define MACROS for the macro version, or don't define it for the fuction-calling version. //#define MACROS class v1 { // The v-type classes use virtual pointers for runtime polymorphism. public: int i; int j; inline virtual void foo() { i = 4; } }; class v2 : public v1 { public: inline virtual void foo() { j = 6; } }; class m1 { // The m-types use my alternative method for runtime polymorphism. protected: int type; public: int k; int l; inline m1() { type = 1; } inline void goo(); inline void koo() { k = 3; } int get_type() { return type; } }; class m2 : public m1 { public: inline m2() { type = 2; } inline void goo(); inline void koo() { l = 5; } }; inline void m1::goo() { switch(type) { case 1: this->m1::koo(); break; case 2: reinterpret_cast <m2 *> (this)->m2::koo(); break; } } inline void m2::goo() { switch(type) { case 1: this->m1::koo(); break; case 2: reinterpret_cast <m2 *> (this)->m2::koo(); break; } } int main() { v1 ob1; v2 ob2; m1 ob3; m2 ob4; v1 *ptr1 = &ob1; v1 *ptr2 = &ob2; m1 *ptr3 = &ob3; m1 *ptr4 = &ob4; clock_t first1; clock_t first2; clock_t hold1; clock_t hold2; clock_t hold3; clock_t hold4; first1 = clock(); for(int i=0;i<10000000;++i) ptr1->foo(); first2 = clock(); hold1 = (first2 - first1); // Place a breakpoint here and start stepping through the assembly instructions to compare differences. first1 = clock(); for(int i=0;i<10000000;++i) ptr2->foo(); first2 = clock(); hold2 = (first2 - first1); #ifdef MACROS first1 = clock(); for(int i=0;i<10000000;++i) FUNC(ptr3); first2 = clock(); hold3 = (first2 - first1); first1 = clock(); for(int i=0;i<10000000;++i) FUNC(ptr4); first2 = clock(); hold4 = (first2 - first1); #else first1 = clock(); for(int i=0;i<10000000;++i) ptr3->goo(); first2 = clock(); hold3 = (first2 - first1); first1 = clock(); for(int i=0;i<10000000;++i) ptr4->goo(); first2 = clock(); hold4 = (first2 - first1); #endif cout << hold1 << '\n' << hold2 << '\n' << hold3 << '\n' << hold4 << '\n'; return 0; }
P.S. I know something about the lack of type safety and the possible problem of programmer logic (which could be helped by thinking of the macro as a global function call rather than a member function). However since I may not fully understand the potential effects, posts about them would not be considered...unwelcome/unnecessary/redundant.