Вопрос про интегрирование Fortran и CPP

vertyal17

Всем привет!
У меня вопрос:
Вобщем написал окукубиблиотеку на Си:

lib.h

extern "C" int PGINIT;
extern "C" void PGINIT111(int * ret);
int PGINIT;
void PGINIT111(int * ret);

lib.cpp

#include "lib.h"
#include "stdio.h"
void PGINIT111(int * ret)
{
printf("VIZOV1");
*ret=-1;
return;
}
int PGINIT
{
printf("VIZOV2");
return(-1);
}

Вобщем была программа на Fortrane. Хочу в ней просто вызывать эти функции. Я в ней просто беру, и в текст программы вставляю:
 

CALL PGINIT111(minit_ret)
WRITE(*,*) ' ret ',minit_ret
minit_ret=PGINIT
WRITE(*,*) ' ret ',minit_ret

Программа компилируется! (без всяких INTERFACE-ов о которых иногда пишут)
Вызов обоих функций происходит (на экран выводятся VIZOV1,2 но почемуто корректно получает возвращаемое значение (-1) только PGINIT111, а PGINIT получает какойто мусор.
В чем проблема?
Еще интересует, почему столь необходимы строки extern "C" PGI..., если убрать их, или даже заменить на extern PGI..., программа в Fortrane уже не компилируется (unresolved external)
Help!

maggi14

потому что если не указано четкого типа именования, функции могут быть скомпилированы с совершенно разнообразными именами и типами передачи параметров/очистки стека.

vertyal17

Почему не помогает extern PGI ?

maggi14

потому что экстерн говорит о том, что где-то в одном из модулей в процессе линковки будет найден PGINIT. Если не пояснить, каким именно образом модифицируются имена, то в реальности один модуль может ждать имени PGINIT_SPEZIALNY_FORMAT_DLYA_PERVOGO_MODULYA, а второй будет предоставлять функцию с именем PGINIT_SPEZIALNY_FORMAT_DLYA_VTOROGO_MODULYA.

vertyal17

Вот такой вопрос, ведь фактически lib собирается из файла .cpp следующего содержания:

extern "C" int PGINIT;
extern "C" void PGINIT111(int * ret);
int PGINIT;
void PGINIT111(int * ret);

#include "stdio.h"

void PGINIT111(int * ret)
{
printf("VIZOV1");
*ret=-1;
return;
}
int PGINIT
{
printf("VIZOV2");
return(-1);
}

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

INTERFACE TO SUBROUTINE PGINIT [C,ALIAS:'_PGINIT' ...

но вместо этого, мне достаточно в с-плюсплюсный же файл вставить описатель функции, тело который в этом же файле лежит. Почему так получается?
Правильно я понимаю, что extern "C" name говорит что функция name (которая тутже и описана является внешней, после компиляции библиотеки, и при подключении этой библиотеки к другому проекту возможно вызывать в файлах другого проекта функцию name без предварительного описания?
Если я прав, как описать что функция связывается согласно стандарту С++?
extern "CPP" int PGINIT;
не работает

vertyal17

Просто я кажется понимаю в чем фишка.
int PGINIT
Компилируется как С++, а в программе на фортране связывается как функция С. Возможно С++ и С поразному возвращают retval, и поэтому я в фортране получаю мусор. Возможно С++ и С одинаковым образом передают параметр-указатель на переменную типа Int, и поэтому несмотря на некорректное связывание, void PGINIT(int * ret) удается передать в фортран корректное значение ret_val.
Моя догадка верна ?
Спасибо

vertyal17

Прочитал.
Убрал все extern "C".
Перекомпилировал библиотеку в стандарте С.
Программа на фортране откомпилировалась (то есть нашла необходимые функции).
Но всеравно вызов PGINIT возвращает мусор (-2147483648 вместо -1). Есть идеи как это можно исправить?

banderon

Попробуй добавить integer PGINIT там, где переменные объявляются. У меня, например, вот это работает:
      program main
implicit none
integer PGINIT
integer minit_ret

CALL PGINIT111(minit_ret)
WRITE(*,*) ' ret ',minit_ret
minit_ret=PGINIT
WRITE(*,*) ' ret ',minit_ret
end

vertyal17

integer PGINIT
Большое спасибо, заработало!
Я так понимаю integer PGINIT это типо прототип функции, говорит фортрану что PGINIT возвращает INTEGER (а не LONG как почемуто решил FORTRAN)?
А как правильно написать прототип для процедуры PGINIT111? Правильно ли это?
INTERFACE TO SUBROUTINE PGINIT111 [C,ALIAS:'_PGINIT111'](retval)
INTEGER retval
END
И что надобыло написать, еслибы у меня была функция PGINIT с параметром?
Например:
int PGINIT (int * val1) ?
Правильно было бы написать так: ?
integer PGINIT (integer VAL1)

banderon

INTERFACE TO SUBROUTINE PGINIT111 [C,ALIAS:'_PGINIT111'](retval)
INTEGER retval
END
Я обычно прототипы не объявляю. Но вроде так, как ты написал, делать можно.
Независимо от количества аргументов у функции, я ее всегда объявляю просто ineteger FUNCTION. То есть можно и НЕ писать вот это:
integer PGINIT (integer VAL1)
А вообще, я в Фортране не очень силен.

vertyal17


А фортран не перепутает тип аргумента? тип возвращаемого значения ведь както перепутал.
И вопрос, что обычно Вы пишите, если функция возвращает void?
ведь
VOID PGINIT
писать в фортране подозреваю нельзя?
В любом случае большое спасибо!

banderon

С типом аргумента все гораздо проще: фортран просто не будет в этом случае проверять правильность. Эта забота целиком ложится на плечи программиста. Главное не перепутать порядок аргументов, и помнить, что фортран передает не значения, а указатели (которые все одного размера по 4/8 байт)
С процедурами, или функциями void func я поступаю просто: CALL имя_процедуры.
А процедуру я вообще никак не объявляю, это уже проблема линкера будет найти ее.

vertyal17

Просто дело в том, что при использовании:
INTERFACE TO SUBROUTINE PGINIT111 [C,ALIAS:'_PGINIT111'](retval)
   INTEGER*2 retval
END
где в С PGINIT111 выглядит так:
extern "C" void PGINIT111(int * ret);
void PGINIT111(int * ret);
void PGINIT111(int * ret)
{
*ret= 119;
return;
}
Фортрановская программа компилируется и валится с ошибкой Access violation во время исполнения, в то время как если INTERFACE ... убрать - все ок
Оставить комментарий
Имя или ник:
Комментарий: