具有非双向参数类型映射的C函数到C ++实例成员蹦床 [英] C func to C++ instance-member trampoline with non-bijective argument type mapping

查看:98
本文介绍了具有非双向参数类型映射的C函数到C ++实例成员蹦床的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:我想我已经破解了,这里

I think I have cracked it, here

(此处非双射表示我们可能具有const char 或char *类型的参数,它们都映射到const std :: string&。 )*

(Non bijective here means we may have arguments of type const char or char* both mapping to const std::string&.)*

注意:我一直在研究这个问题,并且已经问了几天类似的问题。不过,我将从头开始提供一个摘要,因为这可能会使问题成为一个体面的资源。

我有一个C函数指针:

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

以及相关的C ++方法:

And an associated C++ method:

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

我需要创建一种转发机制。

I need to create a mechanism that forwards.

C库将调用 x = slot(& someobject,1, two),我的任务是创建一个 slot 函数,该函数会蹦蹦跳跳到:

The C Library is going to invoke say x = slot(&someobject, 1, "two"), and my task is to create a slot function that will 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);
}

问题是我有接近100个不同的插槽,可能跨越20个不同的签名。因此,我需要使它自动化。

The problem is I have close to 100 different slots, spanning maybe 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 );

当然,这是半伪代码。实际上,语法要复杂得多,因为您需要将 decltype(foofunc),foofunc 传递到模板中-只需 foofunc 是不够的(尽管有可能在C ++ 17中将其修复)。同样,中间模板层对于将函数签名拆分为returntype,C ++ base和args是必需的。并且toCXX(T t)函数将需要重载以将所有必需的A_k映射到X_k。

Of course, that is semi-pseudocode. Actually the syntax is much more complicated, as you need to pass decltype(foofunc), foofunc into a template -- just foofunc alone is not sufficient (although there is some chance this will be fixed in C++17). Also an intermediate template layer is necessary for splitting a function signature into returntype, C++base and args. And a toCXX(T t) function would need to be overloaded to map all necessary A_k to X_k.

我想昨天由于这个答案

解决方案是根据f_cxx的签名构造C函数签名。但是从那以后,我意识到这种方法由于一个令人讨厌的原因而行不通:两个不同的C类型映射到同一个C ++类型。

The solution was to construct the C function signature from f_cxx's signature. However I have since realised that this approach won't work for one annoying reason: Two different C-types map onto the same C++ type.

即插槽功能可能具有涉及 const char * char * 的签名。但是这两个都映射到 const std :: string&上。因此,该技术在遇到 const std :: string&时将失败—它不知道是转换回 char * 还是 const char *

i.e. The slot-function may have a signature involving const char* and char*. But both of these get mapped onto 'const std::string&'. So that technique would fail the moment it comes across 'const std::string&' --it doesn't know whether to convert back to char* or const char*.

因此,我现在尝试使用slot函数的签名来重写它,而不是cxx成员函数。

Hence I'm attempting 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 struggling.

推荐答案


  1. 插槽的地址不是有效的模板参数。我没有花时间确定原因,但是在下面的代码中,我从参数列表中将其删除。在示例代码中未使用它。

  1. The address of the slot is not a valid template parameter. I did not take the time to determine why, but in the code below I removed it from the parameter list. It was not being used in the example code.

您的第一个 decltype 有一个额外的表。其中的表达式无效。删除此选项可以查看所有参数。表达式在被删除之前变成 Table.table.tp_getattr ,这是无效的。

Your first decltype has an extra Table. in it which makes the expression invalid. Removing this allows all the arguments to be seen. The expression became Table.table.tp_getattr before this was removed, which was invalid.

您的 Return 类使用的是C函数的返回类型,但是您将其专门用于C ++函数的返回类型。

Your Return class was using the return type of the C function, but you specialized it on the return type of the C++ function.

您的一些帮助者正在执行诸如将引用返回给临时对象或在需要引用时返回指针的操作。我也对它们进行了清理,但请注意,它会泄漏内存。

Some of your helpers are doing things like returning a reference to a temporary or returning a pointer when a reference was expected. I cleaned those up somewhat as well, but note that it leaks memory.

更新的代码(在g ++ 4.7上编译。 3):

Updated code (compiles 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;
}

这篇关于具有非双向参数类型映射的C函数到C ++实例成员蹦床的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆