fork download
  1. /*
  2. Simulating single inheritance and virtual methods in C
  3.  
  4. 2014/04/24
  5. fred
  6.  
  7. The following Java code will be translated to C:
  8.  
  9. class A
  10. {
  11.   int some_data;
  12.  
  13.   A(int some_data)
  14.   {
  15.   this.some_data = some_data;
  16.   }
  17.  
  18.   void foo()
  19.   {
  20.   System.out.println("foo in A with " + some_data);
  21.   }
  22.  
  23.   void bar()
  24.   {
  25.   System.out.println("bar in A with " + some_data);
  26.   }
  27. }
  28.  
  29. class B extends A
  30. {
  31.   int additional_data;
  32.  
  33.   B(int some_data, int additional_data)
  34.   {
  35.   super(some_data);
  36.   this.additional_data = additional_data;
  37.   }
  38.  
  39.   @Override
  40.   void bar()
  41.   {
  42.   System.out.println("bar in B with " + some_data + " and " + additional_data);
  43.   }
  44.  
  45.   void baz()
  46.   {
  47.   System.out.println("baz in B with " + some_data + " and " + additional_data);
  48.   }
  49. }
  50.  
  51. class C extends B
  52. {
  53.   C(int some_data, int additional_data)
  54.   {
  55.   super(some_data, additional_data);
  56.   }
  57.  
  58.   @Override
  59.   void foo()
  60.   {
  61.   super.foo();
  62.   System.out.println("foo in C with " + some_data + " and " + additional_data);
  63.   }
  64. }
  65. */
  66.  
  67. #include <stdio.h>
  68.  
  69. struct A
  70. {
  71. void * vptr;
  72.  
  73. int some_data;
  74. };
  75.  
  76. /*
  77. memory layout of an A object:
  78.  
  79.  0 vptr (pointing to the vtable for class A)
  80.  4 some_data
  81. */
  82.  
  83. struct B
  84. {
  85. struct A parent;
  86.  
  87. int additional_data;
  88. };
  89.  
  90. /*
  91. memory layout of a B object:
  92.  
  93.  0 vptr (pointing to the vtable for class B)
  94.  4 some_data
  95.  8 additional_data
  96. */
  97.  
  98. struct C
  99. {
  100. struct B parent;
  101. };
  102.  
  103. /*
  104. memory layout of a C object:
  105.  
  106.  0 vptr (pointing to the vtable for class C)
  107.  4 some_data
  108.  8 additional_data
  109. */
  110.  
  111. void A__foo(struct A * this)
  112. {
  113. printf("foo in A with %d\n", this->some_data);
  114. }
  115.  
  116. void A__bar(struct A * this)
  117. {
  118. printf("bar in A with %d\n", this->some_data);
  119. }
  120.  
  121. struct A__vtable_t
  122. {
  123. void (* foo)(struct A *);
  124. void (* bar)(struct A *);
  125. } A__vtable = {
  126. &A__foo,
  127. &A__bar
  128. };
  129.  
  130. void A__init(struct A * this, int some_data)
  131. {
  132. this->vptr = &A__vtable;
  133.  
  134. this->some_data = some_data;
  135. }
  136.  
  137. void invokevirtual__foo(void * this)
  138. {
  139. // Since the vptr is the first data member of each object,
  140. // we can pretend that the this-pointer points to the vptr.
  141. (*(struct A__vtable_t **)this)->foo(this);
  142. // Note how the vtable for A is a prefix of the vtables for B and C,
  143. // so this will also work (and dispatch correctly) on B and C objects.
  144. }
  145.  
  146. void invokevirtual__bar(void * this)
  147. {
  148. (*(struct A__vtable_t **)this)->bar(this);
  149. }
  150.  
  151.  
  152. void B__bar(struct B * this)
  153. {
  154. printf("bar in B with %d and %d\n", this->parent.some_data, this->additional_data);
  155. }
  156.  
  157. void B__baz(struct B * this)
  158. {
  159. printf("baz in B with %d and %d\n", this->parent.some_data, this->additional_data);
  160. }
  161.  
  162. struct B__vtable_t
  163. {
  164. void (* foo)(struct A *);
  165. void (* bar)(struct B *);
  166. void (* baz)(struct B *);
  167. } B__vtable = {
  168. &A__foo,
  169. &B__bar,
  170. &B__baz
  171. };
  172.  
  173. void B__init(struct B * this, int some_data, int additional_data)
  174. {
  175. // statically resolved super constructor call
  176. A__init((struct A *) this, some_data);
  177. // Note that unlike in Java, we could have invoked methods from within the super constructor,
  178. // and they would have dispatched correctly to the A versions,
  179. // because the dynamic type of the object was not B yet...
  180.  
  181. this->parent.vptr = &B__vtable;
  182. // ...and now it is :)
  183.  
  184. this->additional_data = additional_data;
  185. }
  186.  
  187. void invokevirtual__baz(void * this)
  188. {
  189. (*(struct B__vtable_t **)this)->baz(this);
  190. // Again, this will also work correctly for C objects,
  191. // because the vtable for class B is a prefix of the vtable for class C.
  192. // (Actually, they are identical, because class C does not define additional methods.)
  193.  
  194. // Note that if you try to invoke baz on an A object, the behavior is undefined,
  195. // because the vtable for class A has no entry corresponding to baz; it ends at bar.
  196.  
  197. // The vtables for classes A, B and C are likely to be arranged next to each other in memory,
  198. // so invoking baz on an A object will probably have the same effect as invoking foo,
  199. // because foo is the first entry in the vtable for class B, which probably comes after A.
  200. }
  201.  
  202.  
  203. void C__foo(struct C * this)
  204. {
  205. // statically resolved super method call
  206. A__foo((struct A *) this);
  207.  
  208. printf("foo in C with %d and %d\n", this->parent.parent.some_data, this->parent.additional_data);
  209. }
  210.  
  211. struct C__vtable_t
  212. {
  213. void (* foo)(struct C *);
  214. void (* bar)(struct B *);
  215. void (* baz)(struct B *);
  216. } C__vtable = {
  217. &C__foo,
  218. &B__bar,
  219. &B__baz
  220. };
  221.  
  222. void C__init(struct C * this, int some_data, int additional_data)
  223. {
  224. B__init((struct B *) this, some_data, additional_data);
  225.  
  226. this->parent.parent.vptr = &C__vtable;
  227. }
  228.  
  229.  
  230. void test_foo_bar(void * this)
  231. {
  232. printf("---\n");
  233. invokevirtual__foo(this);
  234. printf("---\n");
  235. invokevirtual__bar(this);
  236. printf("---\n\n");
  237. }
  238.  
  239. void test_foo_bar_baz(void * this)
  240. {
  241. printf("---\n");
  242. invokevirtual__foo(this);
  243. printf("---\n");
  244. invokevirtual__bar(this);
  245. printf("---\n");
  246. invokevirtual__baz(this);
  247. printf("---\n\n");
  248. }
  249.  
  250. int main()
  251. {
  252. struct A a;
  253. struct B b;
  254. struct C c;
  255.  
  256. A__init(&a, 1);
  257. B__init(&b, 2, 3);
  258. C__init(&c, 4, 5);
  259.  
  260. test_foo_bar(&a);
  261. test_foo_bar_baz(&b);
  262. test_foo_bar_baz(&c);
  263.  
  264. return 0;
  265. }
  266.  
Success #stdin #stdout 0s 2292KB
stdin
Standard input is empty
stdout
---
foo in A with 1
---
bar in A with 1
---

---
foo in A with 2
---
bar in B with 2 and 3
---
baz in B with 2 and 3
---

---
foo in A with 4
foo in C with 4 and 5
---
bar in B with 4 and 5
---
baz in B with 4 and 5
---