编程语言
首页 > 编程语言> > VC++中的std::function比较问题

VC++中的std::function比较问题

作者:互联网

在VC++中,一个std::function对象会根据情况被编译成多种情况,函数指针,函数对象(lambda),其中还涉及了优化等问题。

对于函数对象来说,常规的手段没有任何办法可以做比较,所以只能去二进制层面看看底层数据。

在VC中可以用同一个结构去模拟std::function对象

struct std_function_struct{

  // vtable[0] 与 vtable[1] 是相同的地址,其中是std::_Func_impl_no_alloc<模板参数...>::_Move地址 或者 std::_Func_impl_no_alloc<模板参数...>::_Copy地址

  // vtable[2] 中保存了 std::_Func_impl_no_alloc<模板参数...>::_Do_call 地址,这个地址是我们的判断依据。

      size_t* vtable;

 

  // 只有函数指针才会保存在这,哪怕lambda不能inline,其地址也会被直接静态编译到_Do_call中

      void* func_ptr; 

      // ... 其他信息对于我们的需求来说并不重要

};

如果是函数指针,很显然的指针就放在 func_ptr 中,比较就它就行。

如果是lambda,我们可以比较vtable[2]是否相同

vtable[2] 中存放的是 std::_Func_impl_no_alloc<模板参数...>::_Do_call() 地址

根据模板参数的不同,哪怕是lambda,std::_Func_impl_no_alloc::_Do_call 也会编译成不同的代码。

其中还涉及了优化问题,两段完全相同代码的lambda,会优化成同一段代码,这不就正好满足了我们需求的判断吗?

但其中有一个比较遗憾的地方,std::function在保存lambda的对象时,func_ptr是不会初始化为0的。

所以我们不能简单的先比较func_ptr再比较vtable[2],我们很大概率是需要先判断两个对象是否存在lambda,好在有std::function::target_type()可以满足我们的要求。

最终代码如下:请注意,这段代码仅适用于VC++,并且不确定随着编译器更新是否能保持二进制一致性。

template<typename _Ty>
bool is_std_func_equal(_Ty& a, _Ty&b) {
    struct __std_func_struct {
        size_t* vt;
        void* func_ptr;
    };
    __std_func_struct* pa = (__std_func_struct*)&a;
    __std_func_struct* pb = (__std_func_struct*)&b;

    if (pa->func_ptr == pb->func_ptr) {
        // 如果func_ptr相等时,我们直接判断两个vtable[2]即可
        return (pa->vt[2] == pb->vt[2]);
    }
    else {
        // 只要func_ptr不相等,我们甭管它保存了什么,都先去类型信息中先查找<lambda_
        bool al = (strstr(a.target_type().name(), "<lambda_") != NULL);
        bool bl = (strstr(b.target_type().name(), "<lambda_") != NULL);
        if (al != bl) // 一个是lambda而另一个不是时,就不需要比较了
            return false;

        if (al) {
            // 两个都是lambda时,比较vtable[2]
            return (pa->vt[2] == pb->vt[2]);
        }
        else {
            // 两个都是函数指针时,比较func_ptr
            return (pa->func_ptr == pb->func_ptr);
        }
    }
}

 

标签:std,function,vtable,struct,C++,func,ptr,lambda
来源: https://www.cnblogs.com/babypapa/p/16384946.html