如何从 C 调用 C++ 方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14815274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:43:00  来源:igfitidea点击:

How to call a C++ method from C?

c++cclassextern

提问by totten

I have a C++ class and I'm compiling it with some C files.

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

I want to call a function which is defined in C++, actually in C++ class, so what am I going to do?

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

The following declarations to show what am I saying: there may there be syntax errors:

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

serial_comm.cpp

serial_comm.cpp

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

external.c

外部文件

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

回答by David Rodríguez - dribeas

The common approach to this problem is providing a C wrapper API. Write a C function that takes a pointer to a MyClassobject (as MyClassis not valid C, you will need to provide some moniker, simplest one is moving void*around) and the rest of the arguments. Then inside C++ perform the function call:

解决此问题的常用方法是提供 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);
}

Then the C code uses the C api to create the object, call the function and release the object:

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

// C
void* myclass = MyClass_create();
MyClass_sendCommandToSerialDevice(myclass,1,2,3);
MyClass_release(myclass);

回答by James Kanze

You'll have to pass an additional argument, with the address of the object to call the function on. Something like:

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

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

mainwill, of course, have to find the instance of the class somehow.

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

EDIT:

编辑:

Concerning some points brought up in other answers:

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

  1. In your example, you compile mainas C. This is undefined behavior, and in practice could mean that your constructors will not be called on static objects. (If your code is in a DLL, you're OK. The standard doesn't say anything about DLL's, but in practice, they work.)

  2. If you're reporting errors by means of exceptions, then you'll have to change your signature to report them in some other way, and wrap your code to catch all exceptions, and convert them to the C convention. (Since your function has no return value, this is easily handled by means of a return code.)

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

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

回答by datenwolf

If you want to do it correct

如果你想正确地做

serial_comm_wrapper.h

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

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);
}

external.c

外部文件

#include "serial_comm_wrapper.h"

int main(int argc, char ** argv) {
     MyClass *instance = MyClass_new();
     MyClass_sendCommandToSerialDevice(instance, ...);
}

回答by Mats Petersson

You can't just go calling C++ code from C.

你不能只是从 C 中调用 C++ 代码。

You will need to produce a C++ interface that can be called from C.

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

Something like this

像这样的东西

 // interface.h

 #ifdef __cplusplus
 extern "C" {
 #endif

 void createMyclass();

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

 void destroyMyclass();

 #ifdef __cplusplus
 extern }
 #endif

Then you do this:

然后你这样做:

 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;
 }

Note that it's IMPERATIVE that you don't let "exceptions" through the wall to the C code, as that is definite undefined behaviour.

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

回答by bames53

You cannot invoke a C++ method directly in C. Instead you may create a C wrapper and then call that:

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

C/C++ compatible header file:

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

implementation (a .cpp file)

实现(.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);
}

main.c

主文件

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

Of course since resource and error handling can be different between C and C++ code you'll have to work out how to handle the combination in your case. For example the above just leaks a MyClassobject instead of cleaning up, and doesn't do anything about exceptions.

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