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

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

Я гуглю эту тему и нахожу этот http://developers.sun.com/solaris/articles/mixing.html

Это говорит:

Можно записать extern "C" функции в C++ тот класс доступа M объекты и называют их из кода C.

Вот функция C++, разработанная для вызова функции членства foo:

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

Мой вопрос состоит в том, куда я помещаю о строке? В моем C++ .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 файл? C++ .h файл? Или C .c файл?

Спасибо.

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

основной c:33:
./some_class.h:24: ошибка: ожидаемый ‘=’, ‘’, ‘;’, ‘asm’ или 'атрибут' перед ‘’ маркер
./some_class.h:25: ошибка: ожидаемый‘)’ перед ‘
’ маркер
./some_class.h:26: ошибка: ожидаемый‘)’ прежде '*' маркер

И вот мои 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; строка.

Любая идея будет очень цениться.

16
задан Azeem 9 July 2017 в 11:55
поделиться

5 ответов

Ваш файл заголовка, который используется совместно вашим кодом C и 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's 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 и 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's 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 по (неконстантной) ссылке (по любой причине). Мы бы определили функцию так:

// 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 «по ссылке».

31
ответ дан 30 November 2019 в 16:42
поделиться

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

Мой вопрос: где мне поставить строку «О нас»? В моем файле c ++ .h? или файл c .h?

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

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

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

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

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

2
ответ дан 30 November 2019 в 16:42
поделиться

Если исключение, созданное DoSomething (); , может привести к тому, что тот же самый метод вызовет еще одно исключение; это может быть проблемой. Другими словами, если DoSomething (); создает исключение из-за того, что вы не обработали предыдущее, может возникнуть нежелательная ошибка.

-121--1672052-

Если эти исключения действительно не связаны друг с другом, так что вы не можете воспользоваться get/setCause () , то я бы предпочел собрать эту информацию в одном MyException .

Например.

public void myFunction() throws MyException {
    MyException myException = null;
    while(stuff) {
        try {
            DoSomething() // throws an exception
        }
        catch (Exception ex) {
            if (myException == null) {
                myException = new MyException();
            }
            myException.addException(some, stuff, of, mine, ex);
        }
    }
    if (myException != null) {
        throw myException;
    }
}

Обновление: Брайан обрабатывает именно этот подход более аккуратным образом. Вместо этого я бы выбрал:)

-121--1672050-

Сайт, на который вы ссылаетесь, уже имеет ответ:

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

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

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

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

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

0
ответ дан 30 November 2019 в 16:42
поделиться

Нужно разделить его между заголовочным и реализационным файлами C++.

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
ответ дан 30 November 2019 в 16:42
поделиться

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

  1. Код C ++ может вызывать любой код C.
  2. Код C обычно не может вызывать какой-либо код C ++.
  3. Функции C могут быть реализованы с помощью кода C ++.

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

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

Однако:

extern "C" void f (int);

будет искажено компилятором C ++ до _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 может реализовать это для вызова любого кода C ++, который ему нравится.

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

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

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

1
ответ дан 30 November 2019 в 16:42
поделиться