OpenRTX/lib/miosix-kernel/miosix/e20/unmember.cpp

147 wiersze
5.7 KiB
C++

/***************************************************************************
* Copyright (C) 2017 by Terraneo Federico *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "unmember.h"
#include <cstdio>
using namespace std;
namespace miosix {
tuple<void (*)(void*), void*> unmemberLogic(unsigned long mixedField,
long thisOffset, unsigned long *o) noexcept
{
//ARM stores the "virtual function or not?" bit in bit #0 of thisOffset and
//multiplies the offset by 2 to make room for it. Intel stores it in bit #0
//of mixedField
#ifdef __ARM_EABI__
//With multiple or virtual inheritance we need to add an offset to this.
o+=(thisOffset & 0xfffffffe)/2/sizeof(long);
//Then we have two cases, we can have a member function pointer to a
//virtual or nonvirtual function. For virtual functions mixedField is the
//offset in the vtable where to find the function pointer.
//For nonvirtual functions it is already the desired function pointer
void (*result)(void*);
if(thisOffset & 1)
{
//Pointer to virtual function. Dereference the object to get to the
//virtual table, and then get the function pointer at the correct offset
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
result=reinterpret_cast<void (*)(void*)>(vtbl[mixedField/sizeof(long)]);
} else {
//Pointer to non virtual function
result=reinterpret_cast<void (*)(void*)>(mixedField);
}
#elif defined(__i386) || defined(__x86_64__)
//With multiple or virtual inheritance we need to add an offset to this.
o+=thisOffset/sizeof(long);
//Then we have two cases, we can have a member function pointer to a
//virtual or nonvirtual function. For virtual functions mixedField is the
//offset in the vtable where to find the function pointer.
//For nonvirtual functions it is already the desired function pointer
void (*result)(void*);
if(mixedField & 1)
{
//Pointer to virtual function. Dereference the object to get to the
//virtual table, and then get the function pointer at the correct offset
unsigned long *vtbl=reinterpret_cast<unsigned long *>(*o);
result=reinterpret_cast<void (*)(void*)>(vtbl[(mixedField-1)/sizeof(long)]);
} else {
//Pointer to non virtual function
result=reinterpret_cast<void (*)(void*)>(mixedField);
}
#else
#error The layout of virtual function pointer is unknown for this arch
#endif
return make_tuple(result,reinterpret_cast<void*>(o));
}
} //namespace miosix
//Testsuite for member function pointer implementation. Compile with:
// g++ -std=c++11 -O2 -DTEST_ALGORITHM -o test unmember.cpp; ./test
#ifdef TEST_ALGORITHM
#include <cstdio>
using namespace std;
using namespace miosix;
class Base
{
public:
void m1() { printf("Base m1 %d %p\n",y,this); }
virtual void m2() { printf("Base m2 %d %p\n",y,this); }
virtual void m3() { printf("Base m3 %d %p\n",y,this); }
int y=1234;
};
class Base2
{
public:
void m4() { printf("Base2 m4 %d %p\n",x,this); }
virtual void m5() { printf("Base2 m5 %d %p\n",x,this); }
int x=5678;
};
class Derived : public Base
{
public:
virtual void m3() { printf("Derived m3 %d %p\n",y,this); }
};
class Derived2 : public Base, public Base2 {};
class A { public: int a=1; };
class B : virtual public A { public: int b=2; };
class C : virtual public A { public: int c=3; void mf() { printf("%d\n",c); } };
class D : public B, public C { public: int d=4; };
void call(tuple<void (*)(void*), void*> a) { (*get<0>(a))(get<1>(a)); }
int main()
{
Base b;
Derived d;
Derived2 d2;
D dd;
call(unmember(&Base::m1,&b));
call(unmember(&Base::m2,&b));
call(unmember(&Base::m3,&b));
call(unmember(&Derived::m3,&d));
call(unmember<Derived2>(&Derived2::m4,&d2));
call(unmember<Derived2>(&Derived2::m5,&d2));
call(unmember<D>(&D::mf,&dd));
}
#endif //TEST_ALGORITHM