c++ 如何把this指標傳入成員函式 像全域性函式一樣呼叫成員函式
阿新 • • 發佈:2019-01-27
測試這個功能的初衷是測試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被破壞了。。。。
g++ -O1 -S test_thisPtr.cpp 生成彙編程式碼#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; }
修改程式碼如下: 把函式的引數都去掉,保證edi不會被用到。然後最重要的一點。把This的型別轉成unsigned long long。不然彙編程式碼中會符號擴充套件。導致依然報segment fault..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
#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