Вызов C ++ из D

Я прошел через документы, объясняющие, как вызвать C ++ из D, объясненный здесь: http://dlang.org/cpp_interface.html . Однако есть пара вещей, которые мне не совсем понятны.

Взяв пример, представленный на сайте D:

#include <iostream>

using namespace std;

class D {
public:
virtual int bar(int i, int j, int k)
{
cout << "i = " << i << endl;
cout << "j = " << j << endl;
cout << "k = " << k << endl;
return 8;
}
};

D *getD() {
D *d = new D();
return d;
}

Класс C ++ может быть вызван из D, как показано ниже:

extern (C++) {
interface D {
int bar(int i, int j, int k);
}

D getD();
}

void main() {
D d = getD();
d.bar(9,10,11);
}

Что мне не совсем понятно, так это то, как объект C ++ удаляется. Вызывает ли сборщик мусора D удаление объекта C ++ или нам нужно предоставить функцию «delete», которая удаляет объект и вызывает его вручную из D? Мне кажется, что если я добавлю деструктор в класс C ++, он никогда не будет вызван. Также я заметил, что класс C ++ должен объявлять функции-члены в том же порядке, в каком они объявлены в интерфейсе D (например, если я добавляю деструктор перед методом bar (), объект C ++ не может быть вызван из D, но если деструктор объявлен после метода bar (), все работает нормально).

Также, если интерфейс D определен как:

extern(C++){
interface D{
int bar();
int foo();
}
}

И соответствующий класс C ++ определяется как:

class D{
public:
virtual int bar(){};
virtual int foo(){};

};

Как вы можете гарантировать, что виртуальные методы C ++ vtbl будут созданы в том же порядке, что и методы, объявленные в интерфейсе D. Для меня нет никаких гарантий для этого. Другими словами, как мы можем быть уверены, что D :: bar () будет в первой позиции в vtbl? Разве это не зависит от реализации / компилятора?

17

Решение

Конкретный способ, которым это реализовано, состоит в том, что у объекта D просто есть совместимая с C ++ vtable. Таким образом, на нем работают только виртуальные функции, и поскольку таблица составлена ​​по индексу, они должны отображаться в том же порядке.

D не имеет ни малейшего представления о конструкторах C ++, деструкторах или каких-либо других специальных методах, но если они виртуальные, он может отбросить vtable.

Я написал небольшую небольшую программу под названием dtoh, ожидающую рассмотрения прямо сейчас, которая может помочь автоматически сгенерировать заголовки C ++ из источника D, чтобы сделать это простым. Это еще не закончено, но это может быть полезно в любом случае:
https://github.com/adamdruppe/tools/blob/7d077b26d991dd5705e834900f66bea737a233b2/dtoh.d

Сначала скомпилируйте это, dmd dtoh.dзатем создайте JSON из вашего файла D: dmd -X yourfile.dзатем беги dtoh yourfile.json и он должен выложить полезный yourfile.h. Но, как я уже сказал, он еще не закончен и все еще ожидает рассмотрения общего дизайна, поэтому он может быть ужасным. Вы всегда можете сделать то, что делаете сейчас, и сделать это самостоятельно.

В любом случае, объект, видимый в D, похож на класс * в C ++. Вы всегда передаете его через указатель, так что конструирование или копирование никогда не выполняются.

D и C ++ также не понимают системы распределения памяти друг друга. Правило, которому я следую, это то, что ваша библиотека создает, ваша библиотека должна быть в состоянии уничтожить. Поэтому, если ваша программа на C ++ обновила ее, убедитесь, что она также удалена и в C ++. Любой объект, который вы создаете в D для передачи в C ++, также должен быть уничтожен D … и вы можете захотеть сделать это вручную. Если ваша функция C ++ хранит ссылку на объект D, но ее нет в D, она может получить мусор! Таким образом, вы либо захотите убедиться, что в D всегда есть живая ссылка на время жизни объекта, либо создать функцию уничтожения самостоятельно с помощью таких функций, как malloc и free.

Мне не нравится, когда вызывающая сторона использует даже generic free (), поскольку версия не обязательно совпадает. Я говорю всегда предоставлять метод из вашей библиотеки, чтобы освободить вещи. Даже если его реализация просто бесплатна (ptr); если вы предоставите свою собственную функцию, это сделает очевидным, что ее следует использовать, и обеспечит вам защиту от таких несоответствий.

4

Другие решения

Я не ожидал бы, что сборщик мусора D будет знать, как освободить объект C ++. Это подразумевает (по крайней мере), что время выполнения D:

  1. сделать предположения о среде выполнения C ++, то есть, как удалить объект C ++
  2. что объект больше не нужен другому коду C ++

Я уверен, что вам придется предоставить еще одну функцию C ++, которая вызывает переданный ей объект. Фактически, многие библиотеки C ++ (даже если они также используются из C ++) имеют такой же шаблон в тех случаях, когда конструктор вызывается изнутри библиотеки. Даже в простом C обычно плохая идея выделить память в одной dll / exe и освободить ее в другой. Это может плохо работать, если два двоичных файла не используют одну и ту же библиотеку времени выполнения.

7

Вам нужно добавить метод в ваш класс D, который вызывает оператор удаления c ++. Или вы можете использовать глобальный метод уничтожения.

Кроме того, не забывайте, что любые интерфейсы к другому языку должны быть объявлены как extern «C», чтобы избежать искажения имени функции компилятора.

#include <iostream>

using namespace std;

class D {
public:
virtual int bar(int i, int j, int k)
{
cout << "i = " << i << endl;
cout << "j = " << j << endl;
cout << "k = " << k << endl;
return 8;
}

// option 1
virtual void destroy()
{
delete this;
}
};

extern "C"D *getD() {
D *d = new D();
return d;
}

// option 2
extern "C"void killD(void* d) {
delete d;
return;
}

Затем в вашем d-коде вам нужно создать выражение области видимости, которое вызывает метод destroy.

2

Поскольку ваш вопрос имеет название «Вызов C ++ из D», я предполагаю, что вы пытаетесь интерфейс к C ++.

Вызывает ли сборщик мусора D удаление объекта C ++ или нам нужно предоставить функцию «delete», которая удаляет объект и вызывает его вручную из D?

Посредством «C ++ объект«Я предполагаю, что вы имеете в виду объект, выделенный с использованием new оператор. D не имеет представления об объектах C ++, созданных с помощью оператора new C ++. Поэтому всякий раз, когда вам нужно удалить объект, выделенный C ++, вы должны предоставить свой собственный код для освобождения памяти.

Поддержка C ++ в D очень ограничена по уважительной причине. — Полная поддержка C ++ означает, что в компилятор D должен быть включен полноценный компилятор C ++ (с препроцессором C ++). Это сделало бы реализацию D-компилятора намного более сложной.

Также я заметил, что класс C ++ должен объявлять функции-члены в том же порядке, в каком они объявлены в интерфейсе D (например, если я добавляю деструктор перед методом bar (), объект C ++ не может быть вызван из D, но если деструктор объявлен после метода bar (), все работает нормально).

В этом конкретном случае, я полагаю, вы сначала пишете класс C ++, имея в виду, что он будет использоваться в проекте D, а затем вы пишете интерфейс D. Интерфейс D должен точно соответствовать методам в классе C ++, потому что компилятор D будет генерировать C ++ -совместимый виртуальный стол.

Поддержка C ++ улучшится, но D вряд ли получит полную поддержку C ++. Была проделана работа по поддержке пространств имен C ++ (улучшение, запрошенное сообществом D).

Поскольку D полностью поддерживает C, лучшая идея состоит в том, чтобы «сгладить» сложный код C ++ до C так, как это делается в статье ».Смешанный C и C ++Msgstr «Давным-давно я использовал подобный подход для вызова методов C ++ из Delphi.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector