【问题标题】:Convert between std::function with different signatures (T* arg to void* arg)在具有不同签名的 std::function 之间转换(T* arg 到 void* arg)
【发布时间】:2015-11-13 16:29:21
【问题描述】:

有没有办法将具有T* 参数的std::function 转换为具有void* 参数的类似参数?这似乎是可能的,因为调用应该在二进制级别兼容。

例如,我怎样才能在不将Producer 转换为模板或失去类型安全性的情况下完成这项工作?

#include <functional>

struct Producer {
    // produces an int from a callable and an address
    template<class Src>
    Producer(Src& src, std::function<int (Src*)> f)
      : arg_(&src),
        f_(f)
    {}

    int operator()() {
        return f_(arg_);
    }

    // type erasure through void* but still type safe since ctor
    // checks that *arg_ and f are consistent
    void* arg_;
    std::function<int (void*)> f_;
};

int func1(char* c) {
    return *c;
}

int func2(int* i) {
    return *i;
}


int try_it() {
    char c = 'a';
    char i = 5;
    // we want to make these work
    Producer p1(c, func1);
    Producer p2(i, func2);
    // but we still want this to fail to compile
    // Producer p3(c, func2);
    return p1() + p2();
}

编辑Solution 具有明确的 UDB,但行为正确。 :-/

【问题讨论】:

  • It seems possible since the calls should be compatible at the binary level. 虽然两个指针在内存中(可能)没有什么不同,但它在某种程度上肯定是 UB。被调用的函数应该如何处理在预期 T 的地方传递的非 T? ...我不明白你为什么在打算失去类型安全时担心失去它。
  • @deviantfan 你确定 UDB 吗?只要来往于void* 的演员有正确的类型,我认为这是合法的。 ctor 阻止获取非 T,但如果它没有然后,它将是 UDB。我不明白你为什么说这不是类型安全的。
  • 我会使用 bind 使函数调用具有相同的签名。这样,Producer 只是调用了一个没有附加参数的函数对象。
  • @AnonMail 有没有办法做到这一点,而 Producer 的用户没有这样做?哦,在出现这种情况的实际情况下,我有两个使用 void* 的函数。

标签: c++ type-conversion c++14 std-function


【解决方案1】:

您可以将参数视为std::function,然后您就不必担心了。采用任何类型F,只要您可以使用Src* 调用它,并且它返回的内容可以转换为int。从对 SFINAE 友好的 std::result_of_t 开始(无耻地从 Yakk 借来):

template<class F, class...Args>
using invoke_result = decltype( std::declval<F>()(std::declval<Args>()...));

然后用它来 SFINAE 你的构造函数:

template<class Src,
         class F,
         class = std::enable_if_t<
            std::is_convertible<invoke_result<F, Src*>, int>::value
         >>
Producer(Src& src, F f)
    : arg_(&src)
    , f_([f = std::move(f)](void* arg){
        return f(static_cast<Src*>(arg));  
    })
{ }

那里没有 UB。这也正确拒绝了您的 p3 案例。此外,您甚至不需要arg_,除非您出于其他原因使用它。将f_ 存储为:

std::function<int ()> f_;

并在其中粘贴Src

template<class Src,
         class F,
         class = std::enable_if_t<
            std::is_convertible<invoke_result<F, Src*>, int>::value
         >>
Producer(Src& src, F f)
    : f_([&src, f = std::move(f)](){
        return f(&src);  
    })
{ }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-27
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-15
    • 1970-01-01
    相关资源
    最近更新 更多