Как вызвать класс С++ и его метод из c файла

Я пытаюсь получить доступ к классу С++ и вызывать его метод из файла .c.

Я google эту тему и найти это http://developers.sun.com/solaris/articles/mixing.html

В нем говорится:

Вы можете написать extern "C" функции в С++, которые будут обращаться к классам M и вызвать их из C-кода.

Вот функция С++, предназначенная для вызова функции-члена foo:

extern "C" int call_M_foo(M* m, int i) { return m->foo(i); }

Мой вопрос в том, где я могу разместить строку about? В моем файле С++ .h? Или C .h файл?

И это продолжается и говорит:

Вот пример кода C, который использует класс M:

struct M;                       // you can supply only an incomplete declaration

int call_M_foo(struct M*, int); // declare the wrapper function

int f(struct M* p, int j)       // now you can call M::foo
{
  return call_M_foo(p, j);
}

Но как/где я должен создать класс M в моем C файле? И где я могу поставить вышеприведенный код? C .h файл? С++ .h файл? Или C .c файл?

Спасибо.

Спасибо за подробный ответ GMan. Я следовал твоему предложению. Но я получаю ошибку компиляции в моем файле .c.

main.c: 33:
./some_class.h:24: error: expected '=,', ';,' asm или ' атрибут до' token
./some_class.h:25: error: expected ') перед' токеном
./some_class.h:26: error: expected ') перед' * токеном

И вот моя строка some_class.h 24-26:

#ifdef __cplusplus 
class M {

public:
  M();
  virtual ~M(); 

  void method1(char* name, char* msg);
};

extern "C" {
#else
struct M;
#endif

  /* access functions line 24-26 are here*/ 
  M* M_new(void);
  void M_delete(M*);
  void M_method1(M*, char*, char*);
#ifdef __cplusplus 
}
#endif

По какой-то причине мой компилятор C не любит extern "C" в GMan оригинале some_test.h. Поэтому я должен изменить это выше. Похоже, компилятор C не любит/понимает строку struct M;.

Любая идея будет высоко оценена.

Ответы

Ответ 1

Ваш заголовочный файл, который делится между вашим кодом C и С++:

#ifdef __cplusplus // only actually define the class if this is C++

class some_class
{
    public:
        int some_method(float);
};

#else

// C doesn't know about classes, just say it a struct
typedef struct some_class some_class; 

#endif

// access functions
#ifdef __cplusplus
    #define EXPORT_C extern "C"
#else
    #define EXPORT_C
#endif

EXPORT_C some_class* some_class_new(void);
EXPORT_C void some_class_delete(some_class*);
EXPORT_C int some_class_some_method(some_class*, float);

Затем ваш исходный файл:

#include "some_foo.h"

int some_class::some_method(float f)
{
    return static_cast<int>(f);
}

// access functions
EXPORT_C some_class* some_class_new(void)
{
    return new some_class();
}

EXPORT_C void some_class_delete(some_class* this)
{
    delete this;
}

EXPORT_C int some_class_some_method(some_class* this, float f)
{
    return this->some_method(f);
}

Теперь скомпилируйте этот источник и ссылку на него. Ваш источник C будет выглядеть примерно так:

#include "some_class.h"

some_class* myInstance = some_class_new();

int i = some_class_some_method(myInstance, 10.0f);

some_class_delete(myInstance);

Если вы серьезно относитесь к смешению C и С++, вам понадобятся макросы.


Вот несколько примеров макросов, которые сделают это намного проще:

// in something like c_export.h
// extern "C" macro
#ifdef __cplusplus
    #define EXPORT_C extern "C"
#else
    #define EXPORT_C
#endif

// new
#define EXPORT_C_CLASS_NEW(classname) EXPORT_C \
            classname * classname##_new(void)

#define EXPORT_C_CLASS_NEW_DEFINE(classname) \
            EXPORT_C_CLASS_NEW(classname) \
            { return new classname (); }

// repeat as much as you want. allows passing parameters to the constructor
#define EXPORT_C_CLASS_NEW_1(classname, param1) EXPORT_C \
            classname * classname##_new( param1 p1)

#define EXPORT_C_CLASS_NEW_1_DEFINE(classname, param1) \
            EXPORT_C_CLASS_NEW_1(classname, param1) \
            { return new classname (p1); }

// delete
#define EXPORT_C_CLASS_DELETE(classname) EXPORT_C \
            void classname##_delete( classname * this)

#define EXPORT_C_CLASS_DELETE_DEFINE(classname) \
            EXPORT_C_CLASS_DELETE(classname) \
            { delete this; }

// functions
#define EXPORT_C_CLASS_METHOD(classname, methodname, ret) EXPORT_C \
            ret classname##_##methodname##( classname * this)

#define EXPORT_C_CLASS_METHOD_DEFINE(classname, methodname, ret) \
            EXPORT_C_CLASS_METHOD(classname, methodname, ret) \
            { return this->##methodname##(); }

// and repeat as necessary.
#define EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) EXPORT_C \
            ret classname##_##methodname( classname * this, param1 p1)

#define EXPORT_C_CLASS_METHOD_1_DEFINE(classname, methodname, ret, param1) \
            EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) \
            { return this->##methodname##(p1); }

И так далее. Наш заголовок/источник становится:

// header
#include "c_export.h" // utility macros

#ifdef __cplusplus // only actually define the class if this is C++

class some_class
{
    public:
        int some_method(float);
};

#else

// C doesn't know about classes, just say it a struct
typedef struct some_class some_class; 

#endif

// access functions
EXPORT_C_CLASS_NEW(some_class);
EXPORT_C_CLASS_DELETE(some_class);
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float);

// source
#include "some_foo.h"

int some_class::some_method(float f)
{
    return static_cast<int>(f);
}

// access functions
EXPORT_C_CLASS_NEW_DEFINE(some_class);
EXPORT_C_CLASS_DELETE_DEFINE(some_class);
EXPORT_C_CLASS_METHOD_1_DEFINE(some_class, some_method, int, float);

И это гораздо более кратким. Это может быть сделано проще (возможно) с переменными макросами, но это нестандартное, и я оставлю это вам.:] Также вы можете сделать макрос для обычных функций, не являющихся членами.


Обратите внимание, что C не знает, какие ссылки есть. Если вы хотите привязываться к ссылке, лучшим вариантом является, вероятно, просто написать определение экспорта вручную. (Но я подумаю об этом, может быть, мы сможем получить его автоматически).

Представьте, что наш some_class взял ссылку float на (не const) ссылку (по какой-либо причине). Мы бы определили такую ​​функцию:

// header
// pass by pointer!                                     v
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) ;

// source
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) 
{
    // dereference pointer; now can be used as reference
    return this->some_method(*p1);
}

И вот мы идем. Вместо этого C будет ссылаться на ссылки с указателями:

// c source, if some_method took a reference:
float f = 10.0f;
int i = some_class_some_method(myInstance, &f);

И мы передаем f "по ссылке".

Ответ 2

Вам нужно разбить его среди заголовков и файлов С++.

foo.h:

extern "C" int call_M_foo(M* m, int i);

foo.cc:

extern "C" int call_M_foo(M* m, int i) {
    return m->foo(i);
}

Чтобы создать объект типа M, вам понадобится аналогичная функция:

foo.h:

struct M;
extern "C" M* create_M();

foo.cc:

extern "C" M* create_M() {
    return new M;
}

Ответ 3

У вас здесь несколько вопросов, поэтому я отвечу им индивидуально.

Мой вопрос в том, где я могу разместить строку about? В моем файле С++.h? или c.h файл?

Строка extern "C" находится в файле С++. Он по сути говорит компилятору: ограничьте все в блоке extern "C" подмножеством C на С++ и функции экспорта, объявленные в этой области соответственно.

Но как/где я должен создать класс M в моем файле c?

Вы не можете. C не имеет понятия классов, и абсолютно нет способ создания экземпляра класса напрямую. Вы, по сути, должны экспортировать функцию C в вашем файле С++, который создает класс и возвращает его как указатель. Затем вы может передать этот указатель вокруг вашего приложения C. Вы не можете класса непосредственно в вашем приложении C, потому что C не поддерживает классы и ваш компилятор С++ может вставлять "скрытые" переменные для ведения бухгалтерского учета внутри фактическое объявление класса.

И где я могу поставить вышеприведенный код?

Кусок кода, который использует указатель struct ure, находится в файле C. Вы принудительно использовать указатель struct ure, потому что C вообще не поддерживает классы. Вы можете отправлять вызовы функций с помощью этой функции в любом месте реализации C файл, как и обычные вызовы функций C.

Ответ 4

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

  • Код С++ может вызывать любой код C.
  • C-код обычно не может вызывать код С++.
  • Функции C могут быть реализованы кодом С++.

Ключевая часть для понимания заключается в том, что компиляторы C и С++ управляют именами функций при создании объектных файлов по-разному, поэтому они обычно не смогут взаимодействовать (во время соединения), за исключением того, что С++ может быть предложено узнать с помощью extern "C"

Прототип: void f(int); может быть искажен компилятором C: _f, но компилятор С++ может выбрать очень другое имя, например f_int, и поэтому компоновщик не знал, что они должны быть одинаковыми.

Однако:

extern "C" void f(int);

будет искажен компилятором С++ до _f, но компилятор C задохнется на extern "C". Чтобы этого избежать, вы должны использовать что-то вроде этого:

#ifdef __cplusplus
extern "C" {
#endif

void f(int);

#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif

Теперь весь вышеперечисленный раздел может находиться в файле .h и, как указано в статье sun.com, представляет собой заголовок смешанного языка.

Это означает, что файл .c или .cpp может #include этот заголовок, а код может вызвать f();
и файл .c или .cpp может #include этот заголовок и реализовать его:

void f()
{
}

Теперь хороший бит в том, что .cpp файл может реализовать это, чтобы вызвать любой код на С++, который ему нравится.

Теперь, чтобы ответить на ваши конкретные вопросы:

  • Первый пример кода может выполняться только в .cpp файле.
  • Второй пример кода может быть только в .c файле.

Кроме того, класс M должен быть объявлен и определен только в файлах С++.

Ответ 5

На вашем сайте есть ответ:

Вы можете объявить функцию печати в заголовочный файл, который совместно используется C и Код С++:

#ifdef __cplusplus extern "C"
#endif int print(int i, double d);

Вы можете объявить не более одной функции перегруженного набора как внешнего "C" Вот пример заголовка C для функции обертки:

int g_int(int); 
double g_double(double);

В принципе, может быть заголовок, разделяемый между двумя, который объявляет прототип функции, добавляя модификатор extern "C", если вы находитесь на С++, чтобы обеспечить доступ к функции в объекте из C. Вы определяете тело функция позже в коде С++, как обычно, при необходимости внутри класса и т.д., и вы используете функцию в C, как обычно.