Thread overview
C++ header generation: Mixin template to create C ABI from D class fails due to pragma(mangle) seeming not to work?
Nov 07, 2021
Gavin Ray
Nov 07, 2021
Adam D Ruppe
Nov 07, 2021
rikki cattermole
November 07, 2021

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:

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

Doesn't actually seem to apply the mangling =/

I've also tried:

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:

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:

$ 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():

// 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);
November 07, 2021
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..
November 08, 2021
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