测试这个功能的初衷是测试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