Skip to content

Instantly share code, notes, and snippets.

@overminder
Last active July 24, 2016 04:32
Show Gist options
  • Save overminder/6d96bf637b1ee32dd62f5dd5d86ffcff to your computer and use it in GitHub Desktop.
Save overminder/6d96bf637b1ee32dd62f5dd5d86ffcff to your computer and use it in GitHub Desktop.
C++: Method pointer
#include <cstdio>
#include "support.hpp"
int main() {
Simple s;
Naive n;
Indirect i(s);
HasName &h = i;
printf("%s\n", h.vName());
i.setBase(n);
printf("%s\n", h.vName());
return 0;
}
%.o : %.cpp
$(CXX) -c $< -o $@ -O3
main : main.o support.o
$(CXX) $^ -o $@
#include "support.hpp"
const char *HasName::name() {
return "Men sheng fa da cai";
}
const char *Simple::vName() {
return "v(Too simple)";
}
const char *Simple::name() {
return "Too simple";
}
const char *Naive::vName() {
return "v(Sometimes naive)";
}
const char *Naive::name() {
return "Sometimes naive";
}
Indirect::Indirect(HasName &base)
: base_(&base)
{
setNamePtr(&HasName::name);
setVNamePtr(&HasName::vName);
}
void Indirect::setBase(HasName &base) {
base_ = &base;
}
void Indirect::setNamePtr(ToCharP method) {
namePtr_ = method;
}
void Indirect::setVNamePtr(ToCharP method) {
vNamePtr_ = method;
}
const char *Indirect::name() {
return (base_->*namePtr_)();
}
const char *Indirect::vName() {
return (base_->*vNamePtr_)();
}
class HasName {
public:
virtual const char *vName() = 0;
const char *name();
};
typedef const char *(HasName::*ToCharP)();
class Simple : public HasName {
public:
virtual const char *vName();
const char *name();
};
class Naive : public HasName {
public:
virtual const char *vName();
const char *name();
};
class Indirect : public HasName {
public:
Indirect(HasName &base);
void setBase(HasName &base);
void setNamePtr(ToCharP method);
void setVNamePtr(ToCharP method);
virtual const char *vName();
const char *name();
private:
HasName *base_;
ToCharP namePtr_;
ToCharP vNamePtr_;
};
support.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <HasName::name()>:
0: b8 00 00 00 00 mov $0x0,%eax
5: c3 retq
6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
d: 00 00 00
0000000000000010 <Simple::vName()>:
10: b8 00 00 00 00 mov $0x0,%eax
15: c3 retq
16: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
1d: 00 00 00
0000000000000020 <Naive::vName()>:
20: b8 00 00 00 00 mov $0x0,%eax
25: c3 retq
26: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
2d: 00 00 00
0000000000000030 <Indirect::vName()>:
30: 48 8b 57 20 mov 0x20(%rdi),%rdx
34: 48 8b 47 28 mov 0x28(%rdi),%rax
38: 48 03 47 08 add 0x8(%rdi),%rax
3c: f6 c2 01 test $0x1,%dl
3f: 48 89 c7 mov %rax,%rdi
42: 74 08 je 4c <Indirect::vName()+0x1c>
44: 48 8b 00 mov (%rax),%rax
47: 48 8b 54 10 ff mov -0x1(%rax,%rdx,1),%rdx
4c: ff e2 jmpq *%rdx
4e: 66 90 xchg %ax,%ax
0000000000000050 <Simple::name()>:
50: b8 00 00 00 00 mov $0x0,%eax
55: c3 retq
56: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5d: 00 00 00
0000000000000060 <Naive::name()>:
60: b8 00 00 00 00 mov $0x0,%eax
65: c3 retq
66: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6d: 00 00 00
# And here we can see vNamePtr's first field is tagged.
0000000000000070 <Indirect::Indirect(HasName&)>:
70: 48 c7 07 00 00 00 00 movq $0x0,(%rdi)
77: 48 89 77 08 mov %rsi,0x8(%rdi)
7b: 48 c7 47 10 00 00 00 movq $0x0,0x10(%rdi)
82: 00
83: 48 c7 47 18 00 00 00 movq $0x0,0x18(%rdi)
8a: 00
8b: 48 c7 47 20 01 00 00 movq $0x1,0x20(%rdi)
92: 00
93: 48 c7 47 28 00 00 00 movq $0x0,0x28(%rdi)
9a: 00
9b: c3 retq
9c: 0f 1f 40 00 nopl 0x0(%rax)
00000000000000a0 <Indirect::setBase(HasName&)>:
a0: 48 89 77 08 mov %rsi,0x8(%rdi)
a4: c3 retq
a5: 90 nop
a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
ad: 00 00 00
00000000000000b0 <Indirect::setNamePtr(char const* (HasName::*)())>:
b0: 48 89 77 10 mov %rsi,0x10(%rdi)
b4: 48 89 57 18 mov %rdx,0x18(%rdi)
b8: c3 retq
b9: 90 nop
ba: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
00000000000000c0 <Indirect::setVNamePtr(char const* (HasName::*)())>:
c0: 48 89 77 20 mov %rsi,0x20(%rdi)
c4: 48 89 57 28 mov %rdx,0x28(%rdi)
c8: c3 retq
c9: 90 nop
ca: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
# Note the `test 1, %rdx`. This seems to be a tag checking on the first word
# of the fat method pointer to see if it's virtual or not.
# If it's not tagged (thus not virtual), then the first word will be used
# directly as the method address. Otherwise, the virtual method address will
# be calculated by *(*(base + method->second) + method->first - 1).
00000000000000d0 <Indirect::name()>:
d0: 48 8b 57 10 mov 0x10(%rdi),%rdx
d4: 48 8b 47 18 mov 0x18(%rdi),%rax
d8: 48 03 47 08 add 0x8(%rdi),%rax
dc: f6 c2 01 test $0x1,%dl
df: 48 89 c7 mov %rax,%rdi
e2: 74 08 je ec <Indirect::name()+0x1c>
e4: 48 8b 00 mov (%rax),%rax
e7: 48 8b 54 10 ff mov -0x1(%rax,%rdx,1),%rdx
ec: ff e2 jmpq *%rdx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment