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?
The answer to the above is yes. header files are simply files in which you can declare your own functions that you can use in your main program or these can be used while writing large C programs. NOTE:Header files generally contain definitions of data types, function prototypes and C preprocessor commands.
You make the declarations in a header file, then use the #include directive in every . cpp file or other header file that requires that declaration. The #include directive inserts a copy of the header file directly into the . cpp file prior to compilation.
Now you can #include your C header without any extern "C" nonsense in your C++ code: // This is C++ code.
You don't need to compile header files. It doesn't actually do anything, so there's no point in trying to run it. However, it is a great way to check for typos and mistakes and bugs, so it'll be easier later.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With