Смешанное программирование — включение заголовка C++ в Fortran

Я пытаюсь использовать функцию из библиотеки, написанной на С++, в своей программе, написанной на Фортране. Библиотека С++ собрана в одном заголовочном файле, поэтому, если вы хотите использовать ее в другой программе на С++, вам нужно сделать только #include functions.h Я хотел бы узнать, как сделать что-то подобное на Фортране.

Из моего исследования я создал этот минимальный жизнеспособный пример:

clib/functions.h:

#ifndef ADD_H
#define ADD_H
extern "C"
{
int __stdcall add(int x, int y);
} 
#endif

clib/functions.cpp:

extern "C"
{
int __stdcall add(int x, int y)
{
    return x + y;
}
}

cinclude.c

#include "clib/functions.h"

интерфейс.f95:

module cinterface
  use,intrinsic::ISO_C_BINDING
  integer(C_INT)::a,b
  interface
    integer(C_INT) function add(a,b) bind(C,name="add")
      use,intrinsic::ISO_C_BINDING
      implicit none
!GCC$ ATTRIBUTES STDCALL :: add
!DEC$ ATTRIBUTES STDCALL :: add
      integer(C_INT), value ::a,b
    end function add
  end interface
end module cinterface

основной.f90

 program main
   use cinterface      
   implicit none
   integer :: c
   c = add(1,2)
   write(*,*) c
 end program

make-файл:

FC = gfortran
CC = g++
LD = gfortran
FFLAGS = -c -O2
CFLAGS = -c -O2
OBJ=main.o 
DEP = \
    cinterface.o cinclude.o

.SUFFIXES: .f90 .f95 .c .o
# default rule to make .o files from .f files
.f90.o  : ;       $(FC) $(FFLAGS) $*.f90 -o $*.o
.f95.o  : ;       $(FC) $(FFLAGS) $*.f95 -o $*.o
.c.o  : ;       $(CC) $(CFLAGS) $*.c -o $*.o
%.o: %.mod
#
main.ex: ${DEP} ${OBJ}
    $(LD)  ${DEP}  ${OBJ} -o prog.exe
#

Когда я пытаюсь сделать этот проект с помощью Cygwin, я получаю следующую ошибку:

main.o:main.f90:(.text+0x13): undefined reference to `add'
main.o:main.f90:(.text+0x13): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `add'
collect2: error: ld returned 1 exit status
make: *** [makefile:19: main.ex] Error 1

Как я могу заставить работать функцию add в Fortran?


person T.Brown    schedule 10.07.2018    source источник
comment
Вам также нужно будет включить объект C++ с функцией при компоновке. Вы, вероятно, имели в виду этот объект, а не cinclude.o? [Может потребоваться больше, но если вы попытаетесь добавить это в ссылку, мы, вероятно, сможем помочь вам больше.]   -  person francescalus    schedule 10.07.2018
comment
@francescalus Извините, вы имеете в виду добавление clib\functions.h в make-файл или добавление туда functions.o из clib\functions.cpp? Потому что в исходной задаче добавление всех полей объектов, созданных из исходных файлов, невозможно (или, по крайней мере, непрактично).   -  person T.Brown    schedule 10.07.2018
comment
Да, скомпилированный код C++ должен быть доступен при линковке (будь то в исполняемом объекте или каким-то динамическим образом). Блок интерфейса Fortran просто говорит, что я обещаю, что существует символ, похожий на этот - вам все равно нужно его предоставить, и это объект C++.   -  person francescalus    schedule 10.07.2018
comment
@francescalus Есть ли способ предоставить Fortran информацию об этих обещанных символах непосредственно из заголовка C++?   -  person T.Brown    schedule 10.07.2018
comment
Компиляторы Fortran обычно не понимают C++, но этот вопрос, возможно, относится к вашему желанию. Однако это отличается от вашей текущей проблемы: ваш интерфейс Fortran уже сообщает вам, что имеет заголовок C++.   -  person francescalus    schedule 10.07.2018
comment
Что сообщает «nm cinlcude.o»? Я подозреваю, что у вас есть какое-то искажение имени add, возможно, для add_. Вам также потребуется добавить атрибут VALUE в интерфейс Fortran для сущностей «a» и «b».   -  person evets    schedule 10.07.2018
comment
@evets nm cinlcude.o сообщает nm: 'cinlcude.o': No such file. Как может произойти искажение add в add_ и как я могу это проверить? что касается значения атрибута, я подозревал, что это не должно создавать проблем при создании проекта, не так ли?   -  person T.Brown    schedule 10.07.2018
comment
Атрибут Value будет необходим, если вы планируете передавать аргументы, не являющиеся массивами и не указателями, в интероперабельную процедуру, чтобы она работала должным образом.   -  person Rodrigo Rodrigues    schedule 10.07.2018
comment
Вам нужен атрибут VALUE для фиктивных аргументов a и b и function add. Также вам понадобится !GCC$ ATTRIBUTES STDCALL :: add, и !DEC$ ATTRIBUTES STDCALL :: add тоже не помешает. Отсутствие атрибута STDCALL может помешать компоновке, если вы компилируете для 32-битной версии.   -  person user5713492    schedule 10.07.2018
comment
@user5713492 user5713492 Я добавил атрибут VALUE к аргументам a,b следующим образом: integer(C_INT),value::a,b, однако я не смог понять, что вы имеете в виду, добавив его к function add. И куда мне поставить !GCC$ ATTRIBUTES STDCALL :: add и !DEC$ ATTRIBUTES STDCALL :: add. Прошу прощения за глупый вопрос.   -  person T.Brown    schedule 10.07.2018
comment
В вашем коде полно фортрана 2003. Убрал из названия бред 95.   -  person Vladimir F    schedule 10.07.2018
comment
Итак, отредактировано. Мне также нужно было отредактировать functions.c, чтобы он летал. Теперь он компилируется, компонуется и работает корректно.   -  person user5713492    schedule 10.07.2018
comment
@user5713492 user5713492 Я попробовал код в этой форме, и он все равно выдает ту же ошибку. Он у вас компилируется?   -  person T.Brown    schedule 10.07.2018
comment
Да, g++ -c functions.cpp gfortran -c cinterface.f90 gfortran main.f90 functions.o -omain построил мне main.exe. Убедитесь, что вы также изменили functions.cpp, а также cinterface.f90.   -  person user5713492    schedule 10.07.2018
comment
@user5713492 user5713492 О, значит, возникла проблема с make-файлом, так как он делает что-то другое.   -  person T.Brown    schedule 10.07.2018


Ответы (3)


Вы прошли большую часть пути. Есть две вещи, которые вам нужно решить, чтобы это заработало: соглашения о связывании и передаче аргументов.

Связь

Как заметил Франческалус, компилятор Fortran не понимает, как анализировать заголовочный файл C/C++. Так что ваши файлы functions.h и cinclude.c в этом примере бесполезны.

Однако пока не выбрасывайте файл functions.h. В нем вы объявляете функцию добавления как:

extern "C"
{
    int __stdcall add(int x, int y);
} 

extern "C" является важной частью. Это сообщает g++, что символы в следующем блоке кода не подлежат всем C++ искажению имен< /а>. Вам понадобится то же самое для определения add в functions.cpp.

extern "C"
{
    int add(int x, int y)
    {
        return x + y;
    }
}

После того, как вы это сделаете, все, что вам нужно будет связать, это functions.o, cinterface.o/mod и main.o.

Соглашения о передаче аргументов

При объявлении add аргументы x и y передаются в функцию по значению. Это поведение по умолчанию для аргументов функции C/C++. Fortran, с другой стороны, по умолчанию передает аргументы функциям/подпрограммам по ссылке. В C++ это будет выглядеть как int add(int* x, int* y). Есть два способа решить эту проблему.

Первый вариант — переопределить вашу функцию добавления с целочисленными указателями на аргументы и разыменовать их внутри функции.

extern "C"
{
    int add(int* x, int* y)
    {
        return *x + *y;
    }
}

Второй вариант (предпочтительный вариант ИМХО) - объявить интерфейс Fortran для передачи аргументов по значению. Они не изменяются в функции add... зачем передавать их по ссылке? Если вы выберете этот вариант, ваш cinterface.f95 должен будет содержать следующее объявление add:

integer(C_INT) function add(a,b) bind(C,name="add")
    use,intrinsic::ISO_C_BINDING
    implicit none
    integer(C_INT),value::a,b
end function add

Обратите внимание на дополнительное украшение value для переменных a и b. Независимо от того, какой вариант вы выберете, без него на моей машине я получаю 8393540 в результате вызова функции add. После рассмотрения соглашений о передаче аргументов я получаю 3, как и ожидалось.

person Kenneth Davis    schedule 10.07.2018
comment
Спасибо за ваш ответ. Однако это не решает общей проблемы с библиотекой С++, которую следует использовать из Фортрана. Я не понимаю структуру .cpp файлов указанной библиотеки. Поэтому я не могу связать их по одному - person T.Brown; 10.07.2018
comment
Ваш вопрос: Как я могу заставить работать функцию добавления в Fortran? Надеюсь, вы нашли хоть какую-то ценность в этом ответе. - person Kenneth Davis; 10.07.2018

Система сборки может значительно упростить это (хотя и за счет внедрения сложной системы сборки). Предполагая макет каталога в вашем вопросе (хотя без cinclude.c, так как я не понимаю, для какой цели он служит)

$ tree
.
├── cinterface.f90
├── clib
│   ├── CMakeLists.txt
│   ├── functions.cpp
│   └── functions.h
├── CMakeLists.txt
└── main.f90

Содержимое файлов cmake

$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 3.9)

project(cpp-add LANGUAGES C CXX Fortran)

add_subdirectory(clib)

add_executable(glue cinterface.f90 main.f90)
target_link_libraries(glue PUBLIC cpp-clib)

и

$ cat clib/CMakeLists.txt 
add_library(cpp-clib functions.cpp)

Затем проект можно настроить и построить обычным образом:

$ cmake -H. -Bbuild && cmake --build build

Исполнение:

$ build/glue
           3
person ptb    schedule 10.07.2018

У меня была такая же проблема, можете ли вы показать нам строку компиляции g++?

Моя проблема была вызвана тем, что мой make-файл не включал должным образом соответствующий файл .o в компиляцию для .exe.

то есть у меня было что-то вроде

Test: Test.cpp dependancy.o 
      g++ Test.cpp -o test.exe

и я получал ту же ошибку, что и вы.

Я решил это, убедившись, что .o действительно используется в строке компиляции.

Test: Test.cpp dependancy.o 
      g++ dependancy.o Test.cpp -o test.exe 

Я предлагаю это, потому что ошибка «Неопределенный символ» обычно означает, что компилятор на самом деле не знает, где находится код для вашего вызова функции добавления.

person Sakkyoku    schedule 16.08.2018