C func for barometer with C ++ instance with type mapping without bijectivity

EDIT: I think I hacked it, here

(Non bijective here means that we can have arguments like const char or char * both mappings in const std :: string &.) *

NOTE. I am working on this and asking similar questions for several days. Nevertheless, I will present the resume from scratch, as this can make the question a worthy resource.

I have a C function pointer:

R_c (*CFunc)( void* self, A1, A2, ..., Ak ) CFunc slot = nullptr; 

And its associated C ++ method:

 class Base { R_cxx f_cxx( X1, X2, ..., Xk ); } 

I need to create a mechanism that forwards.

The C library will call say x = slot(&someobject, 1, "two") , and my task is to create a slot function that will use trampoline to:

 slot( void* self, A1 a1, A2 a2 ) { R_cxx ret = ((Base*)self)->f_cxx( toCxx<A1>(a1), toCXX<A2>(a2) ); return toC<R_cxx>(ret); } 

The problem is that I have about 100 different slots, covering perhaps 20 different signatures. So I need to automate this.

I would start with a template containing a static function:

 template< typename F_c, typename F_Cxx > struct Generator { static Bla call( etc ) {...} }; #define BIND_SLOT( F_c, F_Cxx ) &Generator<F_c,F_Cxx>::call : BIND_SLOT( table->slot35, Base::handler_35 ); 

Of course, this is a semi-pseudo-code. Actually, the syntax is much more complicated, since you need to pass decltype(foofunc), foofunc to the template - just foofunc not foofunc (although there is a chance that this will be fixed in C ++ 17). An intermediate level of the template is also needed to separate the function signature into returntype, C ++ base and args. And the function toCXX (T t) must be overloaded to map all the necessary A_k to X_k.

I thought yesterday this crack cracked thanks to this answer .

The solution was to create a C function signature from the f_cxx signature. However, since then, I realized that this approach will not work for one unfortunate reason: two different types of C for the same type of C ++.

i.e. The slot function may have a signature with const char* and char* . But both of them are mapped to 'const std :: string &'. Thus, this technique will not work when it encounters "const std :: string & --it does not know whether to go back to char* or const char* .

Therefore, I am trying to rewrite it, this time using the signature of the slot function instead of the cxx member function.

However, this is extremely complex code, and I'm afraid.

0
source share
1 answer
  • The slot address is not a valid template parameter. I did not find the time to determine why, but in the code below I removed it from the parameter list. It was not used in the sample code.

  • Your first decltype has an extra Table. which invalidates the expression. Removing this option allows you to see all the arguments. The expression became Table.table.tp_getattr before it was deleted, which was incorrect.

  • The Return class used the return type of the C function, but you specialized in the return type of the C ++ function.

  • Some of your helpers do things such as returning a temporary link or returning a pointer while waiting for a link. I also cleaned them, but note that he is losing his memory.

Updated code (compiled on g ++ 4.7.3):

 #include <iostream> #include <typeinfo> #include <utility> struct PyObject { PyObject(int i_) : i{i_} {} int i; }; struct Object { // avoid implicit conversion explicit Object(PyObject* p_) : p{p_} { std::cout << "Converting PyObject: " << p->i << std::endl; } PyObject* ptr() const { return p; } private: PyObject* p; }; struct Table { typedef PyObject* (*getattrfunc) (PyObject *, char * ); typedef PyObject* (*getattrofunc) (PyObject *, PyObject * ); typedef int (*setattrfunc) (PyObject *, char * , PyObject *); typedef int (*setattrofunc) (PyObject *, PyObject * , PyObject *); getattrfunc tp_getattr; setattrfunc tp_setattr; getattrofunc tp_getattro; setattrofunc tp_setattro; } table{}; class Base { public: Object getattr( const std::string& s ) { std::cout << "Base::getattr" << std::endl; return Object{ new PyObject(42) }; } int setattr( const std::string& s, const Object& value ){ std::cout << "Base::setattr" << std::endl; return 666; } }; class Final : public Base { public: Object getattr( const std::string& s ){ std::cout << "Final::getattr" << std::endl; return Object{ new PyObject(43) }; } } final{}; // helpers template<typename T, typename U> U&& mapperC2CXX( T&& t ) { return std::forward<U&&>(t); } template<typename T> const std::string& mapperC2CXX( char* t ) { return *new std::string(t); } template<typename T> const std::string& mapperC2CXX( const char* t ) { return *new std::string(t); } template<typename T> const std::string& mapperC2CXX( PyObject* p ) { return *new Object{p}; } template<typename T> struct Return { static T&& cvalue(T&& t) { return std::forward<T>(t); } static T cerr() { return T(-1); } }; template<> struct Return<Object> { static PyObject* cvalue(const Object& ob) { return ob.ptr(); } static PyObject* cerr() { return (PyObject*)nullptr; } }; // function generator template<typename Fc, typename Target, Target target> struct Generate; template < typename R , typename ...Arg , typename RTarg , typename ...TargArg , RTarg(Base::*target)(TargArg...) > struct Generate< R(*)(PyObject*, Arg...) , RTarg(Base::*)(TargArg...) , target > { static Base* cxxbase_for(PyObject* pyob) { return (Base*)&final; // cheat for now! } static R call( PyObject* self, Arg... carg) { try { RTarg r_cxx = (cxxbase_for(self)->*target) (mapperC2CXX<Arg>(carg)...); return Return<RTarg>::cvalue(r_cxx); } catch (...) { std::cout << "CAUGHT" << std::endl; return Return<R>::cerr(); } } }; #define BIND(c_slot, cxx_target) c_slot = & Generate< decltype(c_slot), decltype(&cxx_target), &cxx_target >::call; int main() { BIND( table.tp_getattr, Base::getattr ); // test -- imagine C library fires this PyObject* self = (PyObject*)&final; PyObject* pyob = table.tp_getattr( self, (char*)"someattribute" ); std::cout << pyob->i << std::endl; } 
+1
source

Source: https://habr.com/ru/post/1210770/


All Articles