9

我有一个 C++ 类,我正在用一些 C 文件编译它。

我想调用一个在 C++ 中定义的函数,实际上是在 C++ 类中,那我该怎么办?

以下声明表明我在说什么:可能存在语法错误:

serial_comm.cpp

class MyClass {
    void sendCommandToSerialDevice(int Command, int Parameters, int DeviceId) {
         //some codes that write to serial port.
    }
}

外部.c

int main(int argc, char ** argv) {
    //what am I going to write here?
}
4

5 回答 5

20

解决此问题的常用方法是提供 C 包装 API。编写一个 C 函数,它接受一个指向MyClass对象的指针(因为MyClass不是有效的 C,你需要提供一些名字,最简单的一个是移动void*)和其余参数。然后在 C++ 内部执行函数调用:

extern "C" void* MyClass_create() {
   return new MyClass;
}
extern "C" void MyClass_release(void* myclass) {
   delete static_cast<MyClass*>(myclass);
}
extern "C" void MyClass_sendCommandToSerialDevice(void* myclass, int cmd, int params, int id) {
   static_cast<MyClass*>(myclass)->sendCommandToSerialDevice(cmd,params,id);
}

然后C代码使用C api创建对象,调用函数并释放对象:

// C
void* myclass = MyClass_create();
MyClass_sendCommandToSerialDevice(myclass,1,2,3);
MyClass_release(myclass);
于 2013-02-11T15:39:35.490 回答
10

您必须传递一个附加参数,以及要调用该函数的对象的地址。就像是:

extern "C" void SendCommandToSerialDevice( void* object,
    int command, int parameters, int deviceId )
{
    static_cast<MyClass*>( object)->sendCommandToSerialDevice(
        command, parameters, deviceId );
}

main当然,必须以某种方式找到该类的实例。

编辑:

关于其他答案中提出的一些观点:

  1. 在您的示例中,您编译main为 C。这是未定义的行为,实际上可能意味着您的构造函数不会在静态对象上调用。(如果您的代码在 DLL 中,则可以。标准没有说明 DLL 的任何内容,但在实践中,它们可以工作。)

  2. 如果您通过异常报告错误,那么您将不得不更改您的签名以以其他方式报告它们,并包装您的代码以捕获所有异常,并将它们转换为 C 约定。(由于您的函数没有返回值,因此可以通过返回码轻松处理。)

于 2013-02-11T15:38:52.720 回答
2

如果你想做正确的事

serial_comm_wrapper.h

#ifdef __cpluscplus
class MyClass;
extern "C" {
#else
struct MyClass;
typedef struct MyClass MyClass;
#endif

MyClass *MyClass_new();

void MyClass_sendCommandToSerialDevice(MyClass *instance, int Command, int Parameters, int DeviceId);

#ifdef __cpluscplus
}
#endif

serial_comm_wrapper.cc

#include "serial_comm_wrapper.h"
#include "serial_comm.hh"

MyClass *MyClass_new()
{
    return new MyClass();
}

void MyClass_sendCommandToSerialDevice(MyClass *instance, int Command, int Parameters, int DeviceId)
{
    instance->sendCommandToSerialDevice(command, Parameters, DeviceID);
}

外部.c

#include "serial_comm_wrapper.h"

int main(int argc, char ** argv) {
     MyClass *instance = MyClass_new();
     MyClass_sendCommandToSerialDevice(instance, ...);
}
于 2013-02-11T15:37:20.733 回答
1

您不能只从 C 调用 C++ 代码。

您将需要生成一个可以从 C 调用的 C++ 接口。

像这样的东西

 // interface.h

 #ifdef __cplusplus
 extern "C" {
 #endif

 void createMyclass();

 void callMyclassSendCommandToSerialDevice(int Command, int Parameters, int DeviceId);

 void destroyMyclass();

 #ifdef __cplusplus
 extern }
 #endif

然后你这样做:

 static MyClass *myclass;

 void createMyclass()
 {
    try
    {
        myclass = new MyClass;
    }
    catch(...)
    {
       fprintf(stderr, "Uhoh, caught an exception, exiting...\n");
       exit(1);
    }
 }


 void callMyclassSendCommandToSerialDevice(int Command, int Parameters, int DeviceId)
 {
     // May need try/catch here. 
     myclass->sendCommandToSerialDevice(Command, Parameters, DeviceId);
 }

 void destroyMyclass()
 {
    delete myclass;
 }

请注意,不要让“异常”穿过 C 代码的墙,因为那是明确的未定义行为。

于 2013-02-11T15:40:30.367 回答
0

您不能直接在 C 中调用 C++ 方法。相反,您可以创建一个 C 包装器,然后调用它:

C/C++ 兼容头文件:

#ifdef __cplusplus
extern "C" {
#endif

struct MyClass;

MyClass *new_MyClass();

void MyClass_sendCommandToSerialDevice(MyClass *c, int Command, int Parameters, int DeviceID);

#ifdef __cplusplus
} // extern "C"
#endif

实现(.cpp 文件)

#include "my_c_compatiblity_header.h"
#include "MyClass.h"

extern "C" MyClass *new_MyClass() { return new MyClass(); }

extern "C"
void MyClass_sendCommandToSerialDevice(MyClass *c, int Command, int Parameters, int DeviceID) {
    c->sendCommandToSerialDevice(Command, Parameters, DeviceID);
}

主程序

int main(int argc, char ** argv) {
    MyClass *c = new_MyClass();
    MyClass_sendCommandToSerialDevice(c, 1, 0, 123);
}

当然,由于 C 和 C++ 代码之间的资源和错误处理可能不同,因此您必须弄清楚如何在您的情况下处理组合。例如,上面只是泄漏一个MyClass对象而不是清理,并且对异常没有做任何事情。

于 2013-02-11T15:48:20.327 回答