在转换T-s时完美转发非T参数

时间:2015-01-08 14:43:00

标签: c++ c++11 perfect-forwarding

(此问题来自this answer

我正在尝试调整当前只是通过可变数量的参数的trampoline函数。

我想让它将任何参数PyObject* pyob转换为Object{pyob},但转发所有其他参数。

所以(void* self, int, PyObject*, float) - > (int, Object, float)

在该示例中,第一个self参数被删除。这总是发生。在剩下的参数中,其中一个是PyObject *类型,因此需要转换为Object。

这是功能:

template <typename T, T t>
struct trap;

template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{    
    static R 
    call(void* s, Args... args)
    {
        std::cout << "trap:" << typeid(t).name() << std::endl;
        try
        {
            return (get_base(s)->*t)(std::forward<Args>(args)...);
        }
        catch (...)
        {
            std::cout << "CAUGHT" << std::endl;
            return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14); 
        }
    }
};

似乎不是转发参数。我认为它正在制作每个论点的副本。我试过了:

call(void* s, Args&&... args) 

但这只会产生编译错误。

完整的测试用例为here

如何修复函数以完善所有参数,除了它应该转换的PyObject *类型的参数?

2 个答案:

答案 0 :(得分:4)

  

似乎没有转发参数

你不能完全转发函数的参数,这些参数不是模板,或者是通过指向函数的指针调用的,就像你一样。 完美转发涉及模板参数推导,当您通过指针调用函数时不会发生这种情况 - 指针指向具体实例化功能模板。

std::forward<Args>(args)表达式可能会利用 move-constructor 从传递的call参数中复制初始化目标函数的参数>按值(或通过硬编码的右值引用),或让它们受右值引用的约束 - 您不再需要这些实例,您可以自由地移动它们,至少保存一个复制操作。 (它可能像static_cast<Args&&>(args)...一样简单,因为它只是一个参考折叠。)


  

我想让它将任何参数PyObject* pyob转换为Object{pyob},但转发所有其他参数。如何修复函数以完善所有参数,而不是类型为PyObject*的参数,它应该转换?

#include <utility>

template <typename T, typename U>
T&& forward_convert(U&& u)
{
    return std::forward<T>(std::forward<U>(u));
}

template <typename T>
Object forward_convert(PyObject* a)
{
    return Object{a};
}

// ...

return (get_base(s)->*t)(forward_convert<Args>(args)...);

在创建Object函数的签名时,用PyObject*替换任何call,并且只有在有条件地转发或转换参数时,您应该执行以下操作:

template <typename T>
struct replace { using type = T; };

template <>
struct replace<Object> { using type = PyObject*; };

// you may probably want some more cv-ref specializations:
//template <>
//struct replace<Object&> { using type = PyObject*; };  

template <typename T, T t>
struct trap;

template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{    
    static R 
    call(void* s, typename replace<Args>::type... args)
    {
        try
        {
            return (get_base(s)->*t)(forward_convert<typename replace<Args>::type>(args)...);
        }
        catch (...)
        {
            return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14); 
        }
    }
};

DEMO

答案 1 :(得分:2)

您必须将来电更改为(请注意,除了Ts之外,我还会介绍Args)。

template <typename ... Ts>
static R 
call(void* s, Ts&&... args)
{
    std::cout << "trap:" << typeid(t).name() << std::endl;
    try
    {
        return (get_base(s)->*t)(std::forward<Ts>(args)...);
    }
    catch (...)
    {
        std::cout << "CAUGHT" << std::endl;
        return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14); 
    }
}