标签:
测试这个功能的初衷是测试boost里面的bind
boost::bind((&A::sum), &a, _1, _2)
上面的代码是我boost bind及多线程这篇博客里面的一行代码。我就想boost是怎么做到这样调用一个类的成员函数的。其实成员函数和全局函数无非就是差一个this指针参数。给传进去不就也可以调用了。然而并没有那么简单。看了boost的源码表示太长了。没怎么看懂
然后就自己写代码测试了一下。还用了汇编。。
代码参考 http://www.cppblog.com/woaidongmao/archive/2010/03/11/109444.aspx
上面讲有两种调用方法,一种是 把this指针放入ecx寄存器,一种是把this指针当作最后一个参数压入栈
我的电脑环境是ubuntu 64位, 从生成的汇编来看,并不是压入ecx,而是压入rdi。 r开头的寄存器都是64位的。然后我就想模拟成员函数的调用方法,手工压入一个this指针,第一次尝试代码如下。运行报segment fault。查看汇编代码发现。在传递参数后edi被用到了。导致之前手工压入的this被破坏了。。。。
#include <cstdio> using namespace std; class tt{ public: /* void foo(int x) { printf("arg: x=%d\n", x); }*/ void foo(int x, char c = 10, char * s = "hello") { printf("_m_s=%d %d %c %s\n", _m_s, x, c, s); } int _m_s; }; typedef void (tt::* FUNPTR)(int , char, char*); typedef void (*GLOBALPTR)(int , char, char*); template< class ToType, class FromType> void GetMemberFuncAddr(ToType& addr, FromType from) { union{ FromType _f; ToType _t; } ut; //使用union绕过c++的类型检查 ut._f = from; addr = ut._t; } long long This; int main () { tt t; t._m_s =123; char *ptrc = "hello"; FUNPTR ptr = &tt::foo; (t.*ptr)(10, 'a', ptrc); printf("%x\n", ptr); long long p; //p = (int)(&tt::foo); 类型不匹配不能强制类型转换 GetMemberFuncAddr(p, &tt::foo); printf("%x\n", p); GLOBALPTR p1 = (GLOBALPTR)p; // p1(10000, 'c', ptrc); This = (long long)&t; __asm__ ( "movq This, %rdi \n" ); p1(10000, 'c', ptrc); return 0; }g++ -O1 -S test_thisPtr.cpp 生成汇编代码
.file "test_thisPtr.cpp" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "_m_s=%d %d %c %s\n" .section .text._ZN2tt3fooEicPc,"axG",@progbits,_ZN2tt3fooEicPc,comdat .align 2 .weak _ZN2tt3fooEicPc .type _ZN2tt3fooEicPc, @function _ZN2tt3fooEicPc: .LFB30: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movq %rcx, %r9 movsbl %dl, %r8d movl %esi, %ecx movl (%rdi), %edx movl $.LC0, %esi movl $1, %edi movl $0, %eax call __printf_chk addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE30: .size _ZN2tt3fooEicPc, .-_ZN2tt3fooEicPc .section .rodata.str1.1 .LC1: .string "hello" .LC2: .string "%x\n" .text .globl main .type main, @function main: .LFB32: .cfi_startproc subq $24, %rsp .cfi_def_cfa_offset 32 movl $123, (%rsp) //下面5行 显示正常点用成员函数的汇编代码 (t.*ptr)(10, 'a', ptrc) movl $.LC1, %ecx movl $97, %edx movl $10, %esi movq %rsp, %rdi call _ZN2tt3fooEicPc movl $_ZN2tt3fooEicPc, %edx movl $0, %ecx movl $.LC2, %esi movl $1, %edi movl $0, %eax call __printf_chk movl $_ZN2tt3fooEicPc, %edx movl $.LC2, %esi movl $1, %edi movl $0, %eax call __printf_chk movq %rsp, This(%rip) #APP # 49 "test_thisPtr.cpp" 1 movq This, %rdi # 0 "" 2 #NO_APP //下面4行显示p1(10000, 'c', ptrc); 调用的代码 //少了 movq %rsp, %rdi命令,即压入this指针 movl $.LC1, %edx movl $99, %esi movl $10000, %edi call _ZN2tt3fooEicPc movl $0, %eax addq $24, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE32: .size main, .-main .globl This .bss .align 8 .type This, @object .size This, 8 This: .zero 8 .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4" .section .note.GNU-stack,"",@progbits修改代码如下: 把函数的参数都去掉,保证edi不会被用到。然后最重要的一点。把This的类型转成unsigned long long。不然汇编代码中会符号扩展。导致依然报segment fault.
#include <cstdio> using namespace std; class tt{ public: /* void foo(int x) { printf("arg: x=%d\n", x); }*/ void foo() { printf("_m_s=%d %d\n", _m_s); } int _m_s; }; typedef void (tt::* FUNPTR)(); typedef void (*GLOBALPTR)(); template< class ToType, class FromType> void GetMemberFuncAddr(ToType& addr, FromType from) { union{ FromType _f; ToType _t; } ut; ut._f = from; addr = ut._t; } unsigned long long This; int main () { tt t; t._m_s =123; char *ptrc = "hello"; FUNPTR ptr = &tt::foo; (t.*ptr)(); printf("%x\n", ptr); long long p; //p = (int)(&tt::foo); 类型不匹配不能强制类型转换 GetMemberFuncAddr(p, &tt::foo); printf("%x\n", p); GLOBALPTR p1 = (GLOBALPTR)p; // p1(10000, 'c', ptrc); This = (long long)&t; __asm__ ( "movq This, %rdi \n" ); p1(); return 0;
<pre name="code" class="cpp">.file "test_thisPtr.cpp" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "_m_s=%d %d\n" .section .text._ZN2tt3fooEv,"axG",@progbits,_ZN2tt3fooEv,comdat .align 2 .weak _ZN2tt3fooEv .type _ZN2tt3fooEv, @function _ZN2tt3fooEv: .LFB30: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl (%rdi), %edx movl $.LC0, %esi movl $1, %edi movl $0, %eax call __printf_chk addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE30: .size _ZN2tt3fooEv, .-_ZN2tt3fooEv .section .rodata.str1.1 .LC1: .string "%x\n" .text .globl main .type main, @function main: .LFB32: .cfi_startproc subq $24, %rsp .cfi_def_cfa_offset 32 movl $123, (%rsp) movq %rsp, %rdi call _ZN2tt3fooEv movl $_ZN2tt3fooEv, %edx movl $0, %ecx movl $.LC1, %esi movl $1, %edi movl $0, %eax call __printf_chk movl $_ZN2tt3fooEv, %edx movl $.LC1, %esi movl $1, %edi movl $0, %eax call __printf_chk movq %rsp, This(%rip) #APP # 49 "test_thisPtr.cpp" 1 movq This, %rdi # 0 "" 2 #NO_APP call _ZN2tt3fooEv movl $0, %eax addq $24, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE32: .size main, .-main .globl This .bss .align 8 .type This, @object .size This, 8 This: .zero 8 .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4" .section .note.GNU-stack,"",@progbits
c++ 如何把this指针传入成员函数 像全局函数一样调用成员函数
标签:
原文地址:http://blog.csdn.net/zzucaicai/article/details/51333161