在D语言中使用C++类

10

我正在尝试找到一种在D语言中使用C++类的方法。

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

 

D不能调用C++特殊成员函数,反之亦然。这包括构造函数、析构函数、转换运算符、运算符重载和分配器。

因此,我正在尝试将这些 C++ 函数简化为 C 风格的函数调用。以下是我正在使用的证明:

helper.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 ()

GDB能用于D语言吗?至少这样做(或类似的方法)可以告诉你从哪里开始查找。调试器万岁! - Chris Tonkinson
你也可以从SVN构建最新的SWIG并使用它(感谢klickverbot的工作,它支持D语言)。我正在尝试自己学习使用它来包装Irrlicht(在我的空闲时间里)。 - jcao219
我对gdb中的所有选项感到非常不知所措。目前我只知道如何进行回溯。我不确定它还能做什么来帮助我找出问题。 - Bryan Austin
我在编译D文档中的示例时遇到了分段错误。我认为这里可能存在编译器错误。最好汇报一下。 - Winston Ewert
此外,针对 GDB,尝试使用 print variableName。 - Winston Ewert
也许问题出在 someClass *awesomeExample = ... 这一行。你不需要那个星号。如果我没记错的话,这种方式声明了一个指向引用的指针。如果是这种情况,你还应该修改 hearMeOut() 的原型。 - Alexander Malakhov
3个回答

7

你的C++版本返回值。

你的D版本期望它以引用方式返回。

实际上,你的C++版本将someClass的副本放在堆栈上。D认为C++会在堆栈上放置一个指针。它试图将someClass的副本解释为指针,然后发生了糟糕的事情。

问题在于,在D中,类和接口始终通过引用返回。除非你指示其为引用或指针,否则C++将返回所有内容。

因此,你需要这个:

someClass * hearMeOut() { return new someClass; }

不要忘记以后删除它。


+1 是因为这个答案保持了与虚函数的兼容性,而我的答案使得这一点变得困难。 - Billy ONeal
不行,结果还是一样的。 程序收到 SIGSEGV 信号,段错误。 0x0000000000402fa4 在 code/main.d 的 _Dmain 函数中 (args=...): 7 char *shoutoutToTheWorld = awesomeExample.whatSayYou(); - Bryan Austin
@Jacks_Depression,你只能以这种方式调用虚函数。 - Winston Ewert
@Winston:我试图调用的大多数函数都是虚函数。但如果我需要调用一些不是虚函数的函数,是否有与Billy ONeal不同的解决方案? - Bryan Austin
@Winston:gccxml方法大致上就是SWIG所做的。 - Ken Bloom
显示剩余2条评论

3

您没有暴露C接口。您的函数仍然返回一个C++类,而不是C可识别的内容。请将您的类公开为void *。例如:

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的语言)使用。

与其他编程语言不同,D实际上不需要纯C接口。 D允许部分C ++兼容性。请参阅他在问题顶部链接的页面。 - Ken Bloom
@Ken:是的,但如果有人要花时间将其公开为C接口,则应该公开C接口。 ;) D确实知道如何将void *转换为OP想要的任何内容,对吧?(不确定,以前从未编写过D程序) - Billy ONeal
@Billy:使用void可能不是很明智,我更愿意直接使用MyClass——一旦从DLL导出,这并不重要,但有助于在C++世界中保持安全和清晰。 - Puppy
@DeadMG:嗯...我不明白。如果你使用了一些类指针,那么你将无法编译C头文件,因为该类在C中不存在。 :/ - Billy ONeal
@Billy:谢谢,我更喜欢这个解决方案。即使需要打更多的字,但它不那么玄学。 - Bryan Austin
显示剩余2条评论

0

D语言只能通过接口技巧调用虚拟C++方法。

此外,你告诉D hearMeOut()使用了C++的调用约定,而C++具有C调用约定。如果我错了,请纠正我,但这也应该会引起问题。

在我看来,以这种方式与C++进行接口编程实际上仅限于调用简单函数,因为在大多数C++库中,类中始终存在非虚拟方法、运算符和其他方法,更不用提D无法处理的命名空间了。

D SFML采用了Billy所描述的方式。维护C包装器和D包装器是一项繁琐的工作。 应该使用某些(半)自动化的方法,如SWIG,这样还可以获得良好的跨语言多态性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接