Использование класса C++ в D

Я пытаюсь найти способ использовать классы С++ в D.

http://www.digitalmars.com/d/2.0/cpp_interface.html

D не может вызывать специальные функции-члены C++, и наоборот. К ним относятся конструкторы, деструкторы, операторы преобразования, перегрузка операторов и распределители.

Итак, я пытаюсь превратить эти функции С++ в вызовы функций в стиле C. Вот доказательство, с которым я работаю.

помощник.h

class someClass {
    public:
        someClass();
        char *whatSayYou();
};

extern "C"
{
    someClass *hearMeOut();
}

helper.cpp

#include "helper.h"

someClass::someClass()
{

}

char *someClass::whatSayYou()
{
    return "Everything is gravy";
}


someClass *hearMeOut()
{
    return new someClass;
}

main.d

import std.stdio;

int main(string[] args)
{
    someClass *awesomeExample = hearMeOut();
    char *shoutoutToTheWorld = awesomeExample.whatSayYou();
    writefln(std.string.toString(shoutoutToTheWorld));
    return 0;
}


extern (C++)
{
    interface someClass
    {
        char *whatSayYou();
    }

    someClass *hearMeOut();
}

И вот как я это выполнил.

g++-4.3 -c -I code/dg3d_helper -I /usr/local/include/ -o code/dg3d_helper/helper.o code/dg3d_helper/helper.cpp
code/dg3d_helper/helper.cpp: In member function ‘char* someClass::whatSayYou()’:
code/dg3d_helper/helper.cpp:19: warning: deprecated conversion from string constant to ‘char*’
gdc-4.3 -g -c -I code/ -o code/main.o code/main.d
gdc-4.3 -g -I code/ -o main code/dg3d_helper/helper.o code/main.o -lstdc++

И я получаю ошибку сегментации, как только вызывается метод.

Program received signal SIGSEGV, Segmentation fault.
0x0000000000402fa0 in _Dmain (args=...) at code/main.d:7
7       char *shoutoutToTheWorld = awesomeExample.whatSayYou();
(gdb) bt
#0  0x0000000000402fa0 in _Dmain (args=...) at code/main.d:7
#1  0x000000000041b1aa in _D9dgccmain211_d_run_mainUiPPaPUAAaZiZi2goMFZv ()
#2  0x000000000041b235 in _d_run_main ()
#3  0x00002aaaab8cfc4d in __libc_start_main () from /lib/libc.so.6
#4  0x0000000000402d59 in _start ()

person Bryan Austin    schedule 29.11.2010    source источник
comment
Будет ли GDB работать с D? По крайней мере, это [или что-то в этом роде] подскажет, с чего начать поиски. Отладчики FTW   -  person Chris Tonkinson    schedule 30.11.2010
comment
Вы также можете собрать последнюю версию SWIG из SVN и использовать ее (она поддерживает D благодаря работе klickverbot). Я пытаюсь научиться использовать его сам, чтобы обернуть Irrlicht (в свободное время).   -  person jcao219    schedule 30.11.2010
comment
Я очень перегружен всеми опциями в gdb. Все, что я знаю, как сделать на данный момент, это обратная трассировка. Я не уверен, что еще он может сделать, чтобы помочь мне понять проблему.   -  person Bryan Austin    schedule 30.11.2010
comment
Я получаю ошибку сегментации при компиляции примера из документации D. Я думаю, что у вас есть ошибка компилятора здесь. Лучше всего сообщить об этом.   -  person Winston Ewert    schedule 30.11.2010
comment
Также, что касается GDB, попробуйте использовать print variableName   -  person Winston Ewert    schedule 30.11.2010
comment
возможно проблема в someClass *awesomeExample = .... Вам не нужна эта звездочка. IIRC таким образом вы объявляете указатель на ссылку. Если это так, вам также следует изменить hearMeOut() прототип   -  person Alexander Malakhov    schedule 02.12.2010


Ответы (3)


Вы не открыли интерфейс C. У вас по-прежнему есть функция, возвращающая класс C++, а не что-то распознаваемое C. Вместо этого выставьте свой класс как void *s. Например:

class MyClass
{
//private members
public:
//public members
    int MyMethod(int argument) const;
    virtual float MyVirtualMethod();
    virtual ~MyClass() {}
};

class MySecondClass : public MyClass
{
public:
    virtual float MyVirtualMethod();
    int MyMethod2(int argument) const;
};

extern "C" {
    void * CreateMyClass()
    {
        return static_cast<void *>(new(std::nothrow) MyClass);
    }

    void * CreateMySecondClass()
    {
        //Note the cast to the base class first (This is needed
        //because it might actually change the position of the pointer,
        //which would not be automatically adjusted later)
        return static_cast<void *>(static_cast<MyClass *>(new(std::nothrow) MySecondClass));
    }

    int CallMyClassMethod(void * thisMember, int argument)
    {
        return static_cast<MyClass *>(thisMember)->MyMethod(argument);
    }

    float CallMyVirtualMethod(void * thisMember)
    {
        return static_cast<MyClass *>(thisMember)->MyVirtualMethod();
    }

    int CallMyMethod2(void thisMember, int argument)
    {
        MyClass * convertedToMyClass = static_cast<MyClass *>(thisMember);
        MySecondClass * asSecondClass = dynamic_cast<MySecondClass *>(convertedToMyClass);
        if (!asSecondClass)
        {
            //Return error (thisMember not an instance of MySecondClass)
        }
        else
        {
            return asSecondClass->MyMethod2(argument);
        }
    }

    void DestroyMyClass(void * classMember)
    {
        delete static_cast<MyClass *>(classMember);
    }
}

Это сделает ваш класс пригодным для использования в D, а также в C (и любом другом языке, связанном с C).

person Billy ONeal    schedule 29.11.2010
comment
В отличие от любого другого языка программирования, D на самом деле не нуждается в чистом интерфейсе C. D обеспечивает частичную совместимость с C++. См. страницу, на которую он ссылается в верхней части вопроса. - person Ken Bloom; 30.11.2010
comment
@Ken: Да, но если кто-то собирается потратить время на то, чтобы представить его как интерфейс C, ему, вероятно, следует открыть интерфейс C. ;). D знает, как превратить void * в то, что хочет OP, верно? (Не уверен, никогда раньше не программировал на D) - person Billy ONeal; 30.11.2010
comment
@Billy: Использование void *, вероятно, не так уж разумно, я бы предпочел просто использовать прямой MyClass * - это не имеет значения, если вы экспортируете из DLL, но помогает поддерживать безопасность и здравомыслие в мире C ++. - person Puppy; 30.11.2010
comment
@DeadMG: Эээ.. Я не понимаю. Вы не смогли бы скомпилировать заголовок C, если бы использовали какой-либо указатель класса, потому что класс не существовал бы в C. :/ - person Billy ONeal; 30.11.2010
comment
@Billy ONeal: для раскрытия интерфейса требуется небольшая часть работы, если единственное, что вам нужно обернуть, - это конструктор. - person Ken Bloom; 30.11.2010
comment
@Billy: Спасибо, мне это решение нравится немного больше. Даже если это больше печатает, это меньше черной магии. - person Bryan Austin; 30.11.2010
comment
@Ken: Хорошее замечание :) @Jacks_Depression: спасибо :) Одна вещь, которую я забыл в приведенных выше примерах, это обработка исключений - вы не можете выпустить исключения из интерфейса C (но я уверен, что вы уже это знали). (В вашей C-функции будет try/catch, который превращает исключения в коды ошибок) - person Billy ONeal; 30.11.2010

Ваша версия C++ возвращает значение.

Ваша версия D ожидает, что он вернется по ссылке.

По сути, ваша версия C++ помещает копию someClass в стек. D считает, что C++ поместил указатель в стек. Он пытается интерпретировать копию someClass как указатель, и случаются плохие вещи.

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

Таким образом, вам нужно это:

someClass * hearMeOut() { return new someClass; }

Не забудьте потом удалить.

person Winston Ewert    schedule 29.11.2010
comment
+1, потому что этот ответ поддерживает совместимость с виртуальными функциями, что затрудняет мой ответ. - person Billy ONeal; 30.11.2010
comment
Неа. Все тот же результат. Программа получила сигнал SIGSEGV, ошибка сегментации. 0x0000000000402fa4 в _Dmain (args=...) в code/main.d:7 7 char *shoutoutToTheWorld = awesomeExample.whatSayYou(); - person Bryan Austin; 30.11.2010
comment
@Jacks_Depression, таким образом вы можете вызывать только виртуальные функции. - person Winston Ewert; 30.11.2010
comment
@Winston: Большинство функций, которые я пытаюсь вызвать, виртуальные. Но если мне нужно позвонить некоторым, которых нет. Есть ли решение, отличное от решения Billy ONeal? - person Bryan Austin; 30.11.2010
comment
@Jacks_Depression, я не верю, что D поддерживает вызов невиртуальных методов. Вам, вероятно, придется использовать метод Билли. (Если бы это был я, я бы использовал gccxml для анализа моего заголовочного файла C++, использовал полученный файл xml для создания оболочки C для C++ и класса оболочки D для оболочек C. Тогда я мог бы просто использовать свой класс C++ в D. Естественно, но это может потребовать больше усилий, чем того стоит.) - person Winston Ewert; 30.11.2010
comment
@Winston: метод gccxml примерно соответствует тому, что делает SWIG. - person Ken Bloom; 30.11.2010
comment
@ Кен Блум, с помощью swig вам придется поддерживать кучу файлов интерфейса. (если что-то не изменилось). GCCXML должен позволять вам извлекать необходимую информацию из исходных файлов. В остальном они одинаковые - person Winston Ewert; 30.11.2010

D может вызывать виртуальные методы C++ только с помощью трюка с интерфейсом.

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

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

D SFML делает это так, как описал Билли. Утомительно поддерживать оболочку C плюс оболочку D. Следует использовать некоторый (полу-) автоматический подход, такой как SWIG, тогда вы также получите хороший межъязыковой полиморфизм.

person Trass3r    schedule 01.12.2010