【问题标题】:Using C++ class in D在 D 中使用 C++ 类
【发布时间】:2011-05-17 12:56:07
【问题描述】:

我正在尝试找到一种在 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 一起使用吗?至少[或类似的东西]会告诉你从哪里开始寻找。调试器 FTW
  • 您还可以从 SVN 构建最新的 SWIG 并使用它(由于 klickverbot 的工作,它支持 D)。我正在尝试自己学习使用它来包装 Irrlicht(在我的业余时间)。
  • 我对 gdb 中的所有选项感到非常不知所措。在这一点上我所知道的只是回溯。我不确定它还能做些什么来帮助我找出问题。
  • 我得到了从 D 文档编译示例的分段错误。我认为您在这里有一个编译器错误。最好举报。
  • 另外,关于 GDB,请尝试使用 print variableName

标签: c++ class segmentation-fault d


【解决方案1】:

您的 C++ 版本按值返回。

您的 D 版本希望它通过引用返回。

本质上,您的 C++ 版本会将 someClass 的副本粘贴到堆栈上。 D 认为 C++ 会在堆栈上放置一个指针。它试图将 someClass 的副本解释为一个指针,结果发生了不好的事情。

问题在于,在 D 中,类和接口总是通过引用返回。 C++ 按值返回所有内容,除非您指明它是引用还是指针。

因此你需要这个:

someClass * hearMeOut() { return new someClass; }

以后别忘了删除它。

【讨论】:

  • +1 因为这个答案保持与虚函数的兼容性,我的答案很难。
  • 不。结果还是一样。程序收到信号 SIGSEGV,分段错误。 0x0000000000402fa4 in _Dmain (args=...) at code/main.d:7 7 char *shoutoutToTheWorld = awesomeExample.whatSayYou();
  • @Jacks_Depression,你只能这样调用虚函数。
  • @Winston:我试图调用的大多数函数都是虚拟的。但如果我需要调用一些不是的。有比Billy ONeal 不同的解决方案吗?
  • @Jacks_Depression,我不相信 D 支持调用非虚拟方法。你可能不得不使用比利的方法。 (如果是我,我会使用 gccxml 解析我的 C++ 头文件,使用生成的 xml 文件在 C++ 上生成 C 包装器,在 C 包装器上生成 D 包装器类。然后我可以使用我的 C++ 类D 自然。但是这可能是值得的。)
【解决方案2】:

您还没有公开 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 的语言)使用。

【讨论】:

  • 与其他所有编程语言不同,D 实际上并不需要纯 C 接口。 D 允许部分 C++ 兼容性。请参阅他在问题顶部链接的页面。
  • @Ken:是的,但是如果要花时间将其公开为 C 接口,则可能应该公开 C 接口。 ;)。 D does 知道如何将 void * 变成 OP 想要的任何东西,对吧? (不确定,以前从未在 D 中编程过)
  • @Billy:使用 void* 可能不是那么聪明,我宁愿直接使用 MyClass* - 一旦从 DLL 导出就没有关系,但有助于保持安全和健全在 C++ 世界中。
  • @DeadMG:呃……我不明白。如果您使用某些类指针,您将无法编译 C 标头,因为该类在 C 中不存在。:/
  • @Billy ONeal:如果您唯一需要包装的是构造函数,那么公开接口只需要一小部分工作。
【解决方案3】:

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

此外,您告诉 DhearMeOut() 使用了 C++ 调用约定,而 C++ 它具有 C 调用 conv。如果我错了,请纠正我,但这也会产生问题。

Imo 以这种方式与 C++ 交互实际上仅限于调用简单函数,因为在大多数 C++ 库中,您总是在类中拥有非虚拟方法、运算符和其他任何内容,更不用说 D 也无法处理的命名空间。

D SFML 按照比利描述的方式进行。维护 C 包装器和 D 包装器是一项繁琐的工作。 应该使用一些(半)自动方法,如 SWIG,然后您还可以获得很好的跨语言多态性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    相关资源
    最近更新 更多