Отправляет email-рассылки с помощью сервиса Sendsay

Программирование. Форум !!!

Re: Совместить Си с C++ Re[3]: Кто в каком городе

Егор Чулинин George_***@a*****.com писал
p. s.
У меня что-то случилось с рассылкой. Переправте, пожалуйста в лист это
письмо, так как мне кажется, что мои письма туда не попадают :(

Переправляю....=>

Привет!

Есть три разные проблемы и одно замечание.

1) в чём разница между статическими и нестатическими членами класса?
2) как из C-кода вызвать С++ код?
3) как из C-кода сослаться на какой-нибудь C++ объект?

Замечание: Зачем это всё надо и что собственно "это"? :)
//Разберёмся по порядку.

1)
Статические члены класса обладают тем свойством, что разделяются всеми
объектами класса.
То есть статические поля, являются просто глобальными переменными с
хитрыми
именами. (Именно поэтому они нуждаются в отдельном определении, без
которого
не собирается программа. Помните определение "t_filter
*t_filter::p_this;"?). И если обычные поля задаются смещением внутри
объекта
(относительно this), то статические задаются просто адресом.
А статические методы класса являются просто функциями с хитрыми
именами. И
те и другие задаются адресом кода, которые конечно же всегда один, для
любого метода, но в обычный метод неявно передаётся ещё одни параметр --
this (именно поэтому обычные методы могут быть const, это значит, что
this -
ссылается внутри этого метода на объект, который нельзя менять), а в
статический метод ничего неявного (если он возвращает атамарный тип) не
передаётся, так что это простая C-функция, с хитрым именем, которую в
принципе можно пытаться вызвать из C-кода если суметь сослаться на её
адрес.

2)
Итого мы автоматически получаем ответ на второй вопрос: "Из С-кода можно
вызвать только статический метод какого-нибудь С++ класса, если суметь
получить указатели на этот метод, как на функцию и суметь правильно
задать
аргументы". Обычный метод класса вызвать нельзя (во всяком случае
корректным, portable способом). Кроме того, вместо статического метода
можно
вызвать просто C++ функцию.

3)
Теперь как из C-ной части программы сослаться на C++ объекты. Это
зависит от
того, на что мы хотим сослаться -- на переменную или на функцию. Сначала
разберёмся с переменными. К счастью переменные в C b С++ одинаково
(имеется
в виду что код int global_x = 5; породит в обоих языках одинаковый вход
для
линкера). Так что на любую глобальную переменную, которая может быть
описана
и там и там можно сослаться просто -- по имени.
Что касается имён функций, а тем более методов, то есть некоторая
сложность.
И те и другие в C++ могут быть перегружены. Так что в имя функции или
метода, которое попадает во вход линкера запаковывается прототип
функции или
метода. То есть оно выгладит как-то так: _MyFunction@@VGI@bd15X@jg@@.
Понятно, что на подобного крокодила из C-кода сослаться нельзя.
(Очевидно,
что есть и обратная проблема. Если есть охота сослаться на C-функцию из
С++-кода, то не понятно как её описать, чтобы компилятор не породил
вызова
подобного крокодила, а не подставил такое имя, какое принято в C)
Специально для разрешения этой проблемы в C++ есть спциальная
конструкция
extern "C" { }
На все объявленные внутри фигурных скобок функции компилятор будет
ссылаться
так, как это принято в языке C. То есть так можно объявить системное
API, и
оно сможет быть вызвано, а можно объявить функцию, которую хочется
определить в C++ части программы, а иметь возможность позвать как из
С++-части, так и из C-части.
Итого есть два способа сослаться из C-части на С++-код: объявить код,
как
extern "C" или поместить адрес этого кода в переменную, которую видно из
C-части. И опять же два способа передать между C и C++ частями
переменную.
Один из них состоит в том, чтобы завести такую переменную, которая
видна из
обеих частей и, например, инициализировать её указателем на интересующие
данные, а второй состоит в том, чтобы передать указательна данные из C++
части в C-функцию, которую в C++ части объявить как extern "C".
Кроме того, если программа однонитевая и переход C/C++ не рекурсивен, то
часто можно, не сильно всё запутывая передавать данные между C++ кодом,
сквозь C-код при просто через глобальную C++ переменную.
//Например:
//// C++ код:
class CMyClass {
public:
static int StaticField;
int Field;
static int StaticMethod() { return StaticField; }
int Method() const { return Field; }
};

extern "C" {
int CallCMyClassStaticMethod();
}

int CMyClass::StaticField = 0;

static size_t calcFieldOffset( const void* object, const void* field )
//
очень некорректная функция, но делает то, что надо :)
// а именно, если ей передать два указателя (например на объект и
на
его поле), то она вычисляет разницу между ними в байтах.
{
assert( object != 0 );
assert( field != 0 );
const char* objectPtr = reinterpret_cast<const char*>( object
); //
преобразуем к указателю на const char, ничего не проверяя!
const char* fieldPtr = reinterpret_cast<const char*>( field );
//
преобразуем к указателю на const char, ничего не проверяя!
return fieldPtr - objectPtr; // теперь эти указатели
"можно"
вычитать.
}

int f_plus_plus()
{
CMyClass object1;
CMyClass object2;
assert( &object1.Field != &object2.Field );
assert( &object1.StaticField == &object2.StaticField );
assert( calcFieldOffset( &object1, &object1.Field ) ==
calcFieldOffset( &object2, &object2.Field ) );
assert( &object1.StaticField == &CMyClass::StaticField );
assert( CallCMyClassStaticMethod() == CMyClass::StaticField );
return 1;
}

int CallCMyClassStaticMethod()
{
return CMyClass::StaticMethod();
}

static int caller = f_plus_plus();

/* C-код: */

extern int CallCMyClassStaticMethod();

int f()
{
return CallCMyClassStaticMethod();
}
//Замечание:

Я так понял, что потребовалось из С-кода позвать какой-то метод у C++
объекта.
Чаще всего так получается если надо из какого-то, реализованного на C
алгоритма позвать C++ callback. Ну, скажем, у нас есть какая-то
библиотека,
которая, скажем, обрабатывает графические изображения. Пусть у неё есть
функция

typedef int ProcessImageProgressCallback_t( int percentage, void*
callbackData );
int ProcessImage( char* imageFile, ProcessImageProgressCallback_t*
callback,
void* callbackData )

и она когда-то зовёт этот самый callback, через который можно, например,
рисовать progress bar.

Как это всё можно оформить:
/* Общий (C и C++) заголовок ProcessImageProgressCallback_t.h */
typedef int ProcessImageProgressCallback_t( int percentage, void*
callbackData );
extern ProcessImageProgressCallback_t* theProcessImageProgressCallback;
extern void* theProcessImageProgressCallbackData;

#ifdef __cplusplus
extern "C"
#endif//__cplusplus
int ProcessImageProgressCallback( int percentage, void* callbackData );

//// C++ часть
//#include<ProcessImageProgressCallback_t.h>

class CMyCallback {
public:
virtual int Call( int percentage ); // virtual не
обязательно, но в таких конструкциях часто так бывает.
};

int CMyCallback::Call( int )
{
// do something -- типа что-то делает :)
return 1;
}

int ProcessImageProgressCallback( int percentage, void* callbackData )
{
assert( callbackData != 0 );
CMyCallback* callback = reinterpret_cast<CMyCallback*>(
callbackData
);

// Это необязательно, но проверяет, что сюда действительно
дошёл верный указатель
callback = dynamic_cast<CMyCallback*>( callback );
assert( callback != 0 );

return callback->Call( percentage );
}

// Для ссылок из С-части:
static CMyCallback standardCallback;
ProcessImageProgressCallback_t* theProcessImageProgressCallback =
ProcessImageProgressCallback;
void* theProcessImageProgressCallbackData = &standardCallback;

// так можно позвать C-код из C++:
extern "C" int ProcessImage( char* imageFile,
ProcessImageProgressCallback_t* callback, void* callbackData );

int cplusplusCallProcessImage( char* imageFile, CMyCallback* callback )
{
assert( imageFile != 0 );
assert( callback != 0 );
return ProcessImage( imageFile, ProcessImageProgressCallback,
callback );
}

/* а так будет выглядеть C-часть: */

//#include<ProcessImageProgressCallback_t.h>

/* Типа что-то обрабатываем :) */
int ProcessImage( char* imageFile, ProcessImageProgressCallback_t*
callback,
void* callbackData )
{
for( int i = 0; i < 100; i++ )
if( ! callback( i, callbackData ) )
return 0;
return imageFile != 0;
}

/* Если вызов делается из С++ части, то больше вообще
ничего не надо, а вот если из C, то делаем так: */

int cCallProcessImage( char* imageFile )
{
return ProcessImage( imageFile, theProcessImageProgressCallback,
theProcessImageProgressCallbackData );
}

//Ну и последнее, мелкое замечание.
Когда возникает необходимость сопрячь C и C++ код, то обычно всё
получается
более или менее легко, если C++-программа, вызывает C-код. Потому что в
обратном случае обычно начинаются какие-то трудности с вызовом
конструкторов/деструкторов статических объектов и инициализацией
runtime.
Так что это дело в любом случае не особо лёгкое. Как правило лучше всего
перекомпилировать C-код, как C++ (благо синтаксис одного практически
полностью включает синтаксис другого, за одним правда существенным
исключением).

С уважением, Егор Чулинин

Номер выпуска : 2233
Возраст листа : 158 (дней)
Количество подписчиков : 390
Адрес в архиве : http://subscribe.ru/archive/comp.soft.prog.prog/msg/89562
Получить правила : mailto:comp.soft.prog.prog-rules@subscribe.ru
Формат "дайджест" : mailto:comp.soft.prog.prog-digest@subscribe.ru
Формат "каждое письмо" : mailto:comp.soft.prog.prog-normal@subscribe.ru
Формат "читать с веба" : mailto:comp.soft.prog.prog-webonly@subscribe.ru

-*Информационный канал Subscribe.Ru
Написать в лист: mailto:comp.soft.prog.prog-list@subscribe.ru
Отписаться: mailto:comp.soft.prog.prog--unsub@subscribe.ru

http://subscribe.ru/ mailto:ask@subscribe.ru

Ответить   Wed, 25 Feb 2004 12:14:43 +0500 (#89562)