diff test/callback_plain_c++/test_main.cc @ 557:b36a738c8975

- dyncallback: fix for calling back win/x64 C++ methods returning non-trivial aggregates (thanks Raphael!) - test/callback_plain_c++: * added test code for C++ method callbacks returning non-trivial aggregates * makefile linker command fix (was linking assuming c and not c++) - test/plain_c++: comments for completeness
author Tassilo Philipp
date Sat, 20 Aug 2022 21:04:15 +0200
parents 214c4efc104f
children 87b5f5d7af1f
line wrap: on
line diff
--- a/test/callback_plain_c++/test_main.cc	Mon Jul 11 23:17:50 2022 +0200
+++ b/test/callback_plain_c++/test_main.cc	Sat Aug 20 21:04:15 2022 +0200
@@ -111,7 +111,7 @@
 
 char cbNonTrivAggrReturnHandler(DCCallback* cb, DCArgs* args, DCValue* result, void* userdata)
 {
-  printf("reached callback\n");
+  printf("reached callback (for sig \"%s\")\n", *(const char**)userdata);
   dcbReturnAggr(args, result, NULL);    // this sets result->p to the non-triv aggr space allocated by the calling convention
   *(NonTriv*)result->p = NonTriv(1, 3); // explicit non-copy ctor and assignment operator, so not using NonTriv's statics a and b
 
@@ -119,31 +119,49 @@
 }
 
 
+// class with single vtable entry, used to get the compiler to generate a
+// method call; entry is at beginning w/o offset (see plain_c++/test_main.cc
+// about potential vtable entry offsets)
+struct Dummy { virtual NonTriv f() = 0; };
+
+
 int testNonTrivAggrReturnCallback()
 {
   int ret = 1;
 
+  const char *sigs[] = {
+    ")A",   // standard call
+    "_*p)A"  // thiscall w/ this-ptr arg (special retval register handling in win/x64 calling conv)
+  };
+
+
+  for(int i=0; i<sizeof(sigs)/sizeof(sigs[0]); ++i)
   {
-    DCCallback* cb;
+    int is_method = (sigs[i][0] == '_' && sigs[i][1] == '*');
+
     DCaggr *aggrs[1] = { NULL }; // one non-triv aggr
-    cb = dcbNewCallback2(")A", &cbNonTrivAggrReturnHandler, NULL, aggrs);
+    DCCallback* cb = dcbNewCallback2(sigs[i], &cbNonTrivAggrReturnHandler, sigs+i, aggrs);
 
-    NonTriv result = ((NonTriv(*)())cb)(); // potential copy elision on construction
+    DCpointer fakeClass[sizeof(Dummy)/sizeof(sizeof(DCpointer))];
+    fakeClass[0] = &cb; // write method ptr f
+
+    // potential copy elision on construction
+    NonTriv result = is_method ? ((Dummy*)&fakeClass)->f() : ((NonTriv(*)())cb)();
 
     int a = NonTriv::a-1;
     int b = NonTriv::b-1;
-    printf("successfully returned from callback 1/2\n");
+    printf("successfully returned from callback 1/2 of \"%s\"\n", sigs[i]);
     printf("retval w/ potential retval optimization and copy-init (should be %d %d for init or %d %d for copy, both allowed by C++): %d %d\n", 1, 3, a, b, result.i, result.j);
 
     ret = ((result.i == 1 && result.j == 3) || (result.i == a && result.j == b)) && ret;
 
-	// avoid copy elision on construction
-	result.i = result.j = -77;
-	result = ((NonTriv(*)())cb)(); // potential copy elision
+    // avoid copy elision on construction
+    result.i = result.j = -77;
+    result = is_method ? ((Dummy*)&fakeClass)->f() : ((NonTriv(*)())cb)(); // potential copy elision
 
     a = NonTriv::a-1;
     b = NonTriv::b-1;
-    printf("successfully returned from callback 2/2\n");
+    printf("successfully returned from callback 2/2 of \"%s\"\n", sigs[i]);
     printf("retval w/ potential retval optimization and copy-init (should be %d %d for init or %d %d for copy, both allowed by C++): %d %d\n", 1, 3, a, b, result.i, result.j);
 
     dcbFreeCallback(cb);