www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - C++ header generation: Mixin template to create C ABI from D class

reply Gavin Ray <user example.com> writes:
Hello all.

Rikki Cattermole gave me this useful mixin, which can wrap a D 
class and create a "flattened" C ABI for it.

The issue is, trying to use:
```d
pragma(mangle, "create_" ~ __traits(identifier, Type))
void* creator(Parameters!(Type.__ctor) args) {}
```

Doesn't actually seem to apply the mangling =/

I've also tried:

```D
enum ConstructorName(Type) = "create_" ~ __traits(identifier, 
Type);

pragma(mangle, "create_" ~ ConstructorName!(Type))
void* creator(Parameters!(Type.__ctor) args) {}
```

To no success.

The source code I am using is:

```d
import std;

// Try to use an enum to force comptime resolve
enum ConstructorName(Type) = "create_" ~ __traits(identifier, 
Type);

// Compile time mixin function that generates an "extern C" 
flattened ABI for a D class:
mixin template CWrapper(Type)
{
     import std.traits;

export extern (C):

     pragma(mangle, "create_" ~ __traits(identifier, Type))
     void* creator(Parameters!(Type.__ctor) args)
     {
         return cast(void*) new Type(args);
     }

     mixin(() {
         string ret;

         foreach (m; __traits(allMembers, Type))
         {
             static if (m != "__ctor" && m != "__dtor" && 
isFunction!(__traits(getMember, Type, m)))
             {
                 ret ~= `ReturnType!(__traits(getMember, Type, "` 
~ m ~ `")) ` ~ __traits(identifier, Type) ~ `_` ~ m ~
                     `(void* obj, Parameters!(__traits(getMember, 
Type, "` ~ m ~ `")) args) { return (cast(Type)obj).` ~ m ~ 
`(args); }`;
             }
         }

         return ret;
     }());
}

// Sample class to generate C ABI for:
class Foo
{
     int x;
     this(int x) { this.x = x; }
     int getX() { return x; }
     void setX(int x) { this.x = x; }
}
mixin CWrapper!(Foo);
```

Running the below:
```sh
$ dmd -HC=verbose -HCf=main.h main.d
$ ldc2 --HC=verbose --HCf=main_ldc.h main.d
```

Gives the same issue -- the constructor is not mangled, it is 
emitted as `creator()`:

```cpp
// Automatically generated by LDC Compiler v2098

#pragma once

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <math.h>

#ifdef CUSTOM_D_ARRAY_TYPE
#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
#else
/// Represents a D [] array
template<typename T>
struct _d_dynamicArray final
{
     size_t length;
     T *ptr;

     _d_dynamicArray() : length(0), ptr(NULL) { }

     _d_dynamicArray(size_t length_in, T *ptr_in)
         : length(length_in), ptr(ptr_in) { }

     T& operator[](const size_t idx) {
         assert(idx < length);
         return ptr[idx];
     }

     const T& operator[](const size_t idx) const {
         assert(idx < length);
         return ptr[idx];
     }
};
#endif

class Object;

// Ignored template main.ConstructorName(Type) because of linkage
// Ignored template main.CWrapper(Type) because of linkage
// Ignored class main.Foo because of linkage
extern "C" void* creator(int32_t _param_0);

extern "C" int32_t Foo_getX(void* obj);

extern "C" void Foo_setX(void* obj, int32_t _param_1);

extern "C" _d_dynamicArray< const char > Foo_toString(void* obj);

extern "C"  Foo_toHash(void* obj);

extern "C" int32_t Foo_opCmp(void* obj, Object* _param_1);

extern "C" bool Foo_opEquals(void* obj, Object* _param_1);

extern "C" Object* Foo_factory(void* obj, _d_dynamicArray< const 
char > _param_1);
```
Nov 07 2021
next sibling parent Adam D Ruppe <destructionator gmail.com> writes:
On Sunday, 7 November 2021 at 19:25:43 UTC, Gavin Ray wrote:
 pragma(mangle, "create_" ~ __traits(identifier, Type))
 void* creator(Parameters!(Type.__ctor) args) {}
That would create a function with the literal mangle create_TYPENAMEHERE which isn't C++ compatible. If you had an `extern "C" whatever create_TYPENAMEHERE(whateveR);` then that mangle would tie in. You might just want to use an extern(C++) constructor here..
Nov 07 2021
prev sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 08/11/2021 8:25 AM, Gavin Ray wrote:
 Hello all.
 
 Rikki Cattermole gave me this useful mixin, which can wrap a D class and 
 create a "flattened" C ABI for it.
 
 The issue is, trying to use:
 ```d
 pragma(mangle, "create_" ~ __traits(identifier, Type))
 void* creator(Parameters!(Type.__ctor) args) {}
 ```
 
 Doesn't actually seem to apply the mangling =/
```d class Type { this() {} } pragma(msg, creator.mangleof); // _D9onlineapp7creatorFZPv pragma(mangle, "create_" ~ __traits(identifier, Type)) void* creator(Parameters!(Type.__ctor) args) { pragma(msg, creator.mangleof); // create_Type return cast(void*)(new Type(args)); } pragma(msg, creator.mangleof); // create_Type ``` Order matters for this stuff unfortunately. Now with regards to -HC That is certainly a bug with the generator. ```asm .text.create_Type segment assume CS:.text.create_Type create_Type: push RBP mov RBP,RSP sub RSP,010h mov RDI,onlineapp.Type.__Class GOTPCREL[RIP] call _d_newclass PLT32 mov -8[RBP],RAX mov RDI,RAX call onlineapp.Type onlineapp.Type.__ctor() PLT32 leave ret add [RAX],AL .text.create_Type ends ``` https://issues.dlang.org/show_bug.cgi?id=22489
Nov 07 2021