Reputation: 89
I am trying to use a function from library written in C++ in my program written in Fortran. The C++ library is summarized in one header file so that if you want to use it in another C++ program you only do #include functions.h
I would like to find out how to do something similar in Fortran.
From my research I've created this minimal viable example:
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"
cinterface.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
main.f90
program main
use cinterface
implicit none
integer :: c
c = add(1,2)
write(*,*) c
end program
makefile:
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
#
When I try to make this project using Cygwin I get a following error:
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
How can I make add
function in Fortran work?
Upvotes: 5
Views: 1972
Reputation: 1
I was having the same issue, can you show us the g++ compile line?
My issue was caused because my make file didn't properly include the appropriate .o file within the compilation for the .exe
i.e I had something like
Test: Test.cpp dependancy.o
g++ Test.cpp -o test.exe
and I was getting the same error returned that you did.
I solved it by ensuring the .o was actually used on the compile line.
Test: Test.cpp dependancy.o
g++ dependancy.o Test.cpp -o test.exe
I suggest this because the error "Undefined Symbol" typically means the compiler doesn't actually know where the code is for your call to the add function.
Upvotes: 0
Reputation: 2148
A build system can simplify this significantly (albeit at the cost of introducing a complex build system). Assuming the directory layout in your question (although without the the cinclude.c
since I don't see what purpose it serves)
$ tree
.
├── cinterface.f90
├── clib
│ ├── CMakeLists.txt
│ ├── functions.cpp
│ └── functions.h
├── CMakeLists.txt
└── main.f90
The contents of the The cmake files are
$ 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)
and
$ cat clib/CMakeLists.txt
add_library(cpp-clib functions.cpp)
The project can then be configured and built in the usual way:
$ cmake -H. -Bbuild && cmake --build build
Execution:
$ build/glue
3
Upvotes: 1
Reputation: 167
You are most of the way there. There are two things you need to address to make this work: linkage and argument passing conventions.
Linkage
As francescalus noted, the Fortran compiler doesn't understand how to parse a C/C++ header file. So your functions.h and cinclude.c files aren't going to be of any use in this example.
Don't throw away your functions.h yet, though. In it you declare the add function as:
extern "C"
{
int __stdcall add(int x, int y);
}
The extern "C"
is the important part. This tells g++ that the symbols in the following block of code aren't subject to all of the C++ name mangling. You'll need the same surrounding the add
definition in functions.cpp.
extern "C"
{
int add(int x, int y)
{
return x + y;
}
}
Once you've done that all you will need to link are functions.o, cinterface.o/mod, and main.o.
Argument passing conventions
The way add
is declared the arguments x
and y
are passed to the function by value. That is the default behavior for C/C++ function arguments. Fortran, on the other hand, defaults to passing arguments to functions/subroutines by reference. In C++ this would look like int add(int* x, int* y)
. There are two ways to address this problem.
The first option is to redefine your add function with integer pointers for the arguments and dereference them inside the function.
extern "C"
{
int add(int* x, int* y)
{
return *x + *y;
}
}
The second option (IMHO the preferred option) is to declare the Fortran interface to pass the arguments by value. They aren't being modified in the add
function...why pass them by reference? If you choose this option, then your cinterface.f95 will need to contain the following declaration of 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
Note the additional value
decoration on the variables a
and b
. No matter which option you go with, without it on my machine I get 8393540 printed out as the result of the add
function call. After addressing the argument passing conventions I get 3 printed out as expected.
Upvotes: 4