Thread overview
C++ interop, abstract struct problem
Dec 28, 2020
RSY
Dec 28, 2020
RSY
Dec 28, 2020
Paul Backus
Dec 28, 2020
RSY
Dec 29, 2020
RSY
Dec 29, 2020
RSY
December 28, 2020
Hello

I try to use a C++ lib


So far so good, i managed to use that lib and get started

The problem however is this:

C++ API:
```
void preinit(IAllocator& allocator, bool load_renderdoc);
```


``IAllocator`` is an abstract struct, (a struct with virtual functions)

But the problem is D doesn't allow that, so apparently i need to use an abstract class and wrap it using: ``extern (C++, struct)``

The problem is, when passing the object to the function i get:

``dumix.obj : error LNK2019: unresolved external symbol "void __cdecl Lumix::gpu::preinit(struct Lumix::IAllocator *,bool)" (?preinit@gpu@Lumix@@YAXPEAUIAllocator@2@_N@Z) referenced in function _Dmain ``


Wich is wrong, it is supposed to pass as a reference, i don't know why it is picky

Do you guys have an idea what i do wrong, or what i should do?

Thanks!

Here is the full code:


```
import std.stdio;
import core.thread;
import core.stdc.stdio;
import core.memory;

extern (C++, Lumix) @nogc nothrow
{

    extern (C++, os) @nogc nothrow
    {
        struct InitWindowArgs
        {
            enum Flags
            {
                NO_DECORATION = 1 << 0,
                NO_TASKBAR_ICON = 1 << 1
            }

            const(char)* name = "hello lumix";
            bool handle_file_drops = false;
            bool fullscreen = false;
            uint flags = 0;
            void* parent = null;
        }

        void* createWindow(const ref InitWindowArgs);
    }

    align(8) struct Mutex
    {
        ubyte[8] data;
    }


    extern (C++, struct) abstract class IAllocator
    {
        void* allocate(size_t n);
        void  deallocate(void* p);
        void* reallocate(void* ptr, size_t size);
        void* allocate_aligned(size_t size, size_t alignn);
        void  deallocate_aligned(void* ptr);
        void* reallocate_aligned(void* ptr, size_t size, size_t alignn);
    }

    extern (C++, struct) class DefaultAllocator : IAllocator
    {
        ubyte* m_small_allocations = null;
        void*[4] m_free_lists;
        uint m_page_count = 0;
        Mutex m_mutex;

        override void* allocate(size_t n);
        override void  deallocate(void* p);
        override void* reallocate(void* ptr, size_t size);
        override void* allocate_aligned(size_t size, size_t alignn);
        override void  deallocate_aligned(void* ptr);
        override void* reallocate_aligned(void* ptr, size_t size, size_t alignn);
    }

    extern (C++, gpu) @nogc nothrow
    {
        enum InitFlags : uint
        {
            NONE = 0,
            DEBUG_OUTPUT = 1 << 0,
            VSYNC = 1 << 1
        }

        // 1505C2 ?preinit@gpu@Lumix@@YAXAEAUIAllocator@2@_N@Z
        void preinit(IAllocator allocator, bool load_renderdoc);
        bool init(void* window_handle, InitFlags flags);
        void* allocProgramHandle();
    }
}

void main()
{
    auto arg = InitWindowArgs();
    auto win = Lumix.os.createWindow(arg);

    IAllocator allocator = new DefaultAllocator();
    Lumix.gpu.preinit(allocator, false);
    Lumix.gpu.init(win, InitFlags.NONE);

    while (true)
    {
        Thread.sleep(usecs(1));
    }
}

```
December 28, 2020
IAllocator struct: https://github.com/nem0/LumixEngine/blob/master/src/engine/allocator.h#L18


function: https://github.com/nem0/LumixEngine/blob/master/src/renderer/gpu/gpu.h#L208
December 28, 2020
On Monday, 28 December 2020 at 15:42:26 UTC, RSY wrote:
> ``IAllocator`` is an abstract struct, (a struct with virtual functions)
>
> But the problem is D doesn't allow that, so apparently i need to use an abstract class and wrap it using: ``extern (C++, struct)``

You could try using one of the techniques on this page to make `IAllocator` a struct instead of a class:

https://dlang.org/spec/cpp_interface.html#structs
December 28, 2020
On Monday, 28 December 2020 at 16:42:19 UTC, Paul Backus wrote:
> On Monday, 28 December 2020 at 15:42:26 UTC, RSY wrote:
>> ``IAllocator`` is an abstract struct, (a struct with virtual functions)
>>
>> But the problem is D doesn't allow that, so apparently i need to use an abstract class and wrap it using: ``extern (C++, struct)``
>
> You could try using one of the techniques on this page to make `IAllocator` a struct instead of a class:
>
> https://dlang.org/spec/cpp_interface.html#structs

Oh i wonder how i could have missed this part, thanks!! i will try this
December 29, 2020
Hmm, something seems to be very wrong, here what i got so far

``` D
    struct IAllocator
    { }
    struct DefaultAllocator
    {
        // BASE --------------------------
        IAllocator base = IAllocator();
        alias base this;
        //--------------------------------

        ubyte* m_small_allocations = null;
        void*[4] m_free_lists;
        uint m_page_count = 0;
        Mutex m_mutex;
    }


    void preinit(ref IAllocator allocator, bool load_renderdoc);
```

The problem is the allocator data seems to be corrupted, it crashes on the C++ side when calling preinit


IAllocator is empty, but it doesn't get optimized as the wiki say, since the size of DefaultAllocator is 64 bytes on the C++ side, and 56 bytes on D side, i get 64 bytes with the definition above

Does anyone have an idea, did i translate the struct wrong?


```C++
struct LUMIX_ENGINE_API IAllocator {
	virtual ~IAllocator() {}
	virtual bool isDebug() const { return false; }

	virtual void* allocate(size_t size) = 0;
	virtual void deallocate(void* ptr) = 0;
	virtual void* reallocate(void* ptr, size_t size) = 0;

	virtual void* allocate_aligned(size_t size, size_t align) = 0;
	virtual void deallocate_aligned(void* ptr) = 0;
	virtual void* reallocate_aligned(void* ptr, size_t size, size_t align) = 0;

	template <typename T> void deleteObject(T* ptr) {
		if (ptr)
		{
			ptr->~T();
			deallocate_aligned(ptr);
		}
	}
};

```

and for DefaultAllocator

```c++
struct LUMIX_ENGINE_API DefaultAllocator final : IAllocator {
	struct Page;

	DefaultAllocator();
	~DefaultAllocator();

	void* allocate(size_t n) override;
	void deallocate(void* p) override;
	void* reallocate(void* ptr, size_t size) override;
	void* allocate_aligned(size_t size, size_t align) override;
	void deallocate_aligned(void* ptr) override;
	void* reallocate_aligned(void* ptr, size_t size, size_t align) override;

	u8* m_small_allocations = nullptr;
	Page* m_free_lists[4];
	u32 m_page_count = 0;
	Mutex m_mutex;
};

```


For Mutex:

```c++
struct alignas(8) LUMIX_ENGINE_API Mutex {
	friend struct ConditionVariable;
	
	Mutex();
	Mutex(const Mutex&) = delete;
	~Mutex();

	void enter();
	void exit();

private:
	#ifdef _WIN32
		u8 data[8];
	#else
		pthread_mutex_t mutex;
	#endif
};

```

December 29, 2020
Here is a debugger view of the passed IAllocator&

https://i.imgur.com/p04Tj4a.png