link fortran and c++ using CMake - skipping incompatible ... Error - c++

One colleague did send me a Fortran function to include in my C++ program.
So far, everything in my program is coded in C++.
To keep things simple (especially dependencies and installation) I thought I'll just re-code it in C++.
Unfortunately, the code is very complex with many goto statements and other stuff I'm not very familiar with. (I have never worked with Fortran and this is from an old scientific Fortran 77 program)
Thus, I would like to call the Fortran function directly in C++.
A prerequisite is, that I'm using CMake for my program and everything (like linking) has to be done in the CMake file. Additionally, the CMake file should be as simple as possible since only scientists work and extend the program with no sophisticated programming background.
I found many approaches and solutions on the internet - however, most are very complex dealing with modules and libraries - I only need to call one function, we are not working with libraries or such.
Unfortunately, I get a lot of errors when executing my code:
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
skipping incompatible C:/MinGW/lib/gcc/mingw32/6.3.0/libgfortran.dll.a
when searching for -lgfortran
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
skipping incompatible C:/MinGW/lib/gcc/mingw32/6.3.0/libgfortran.a
when searching for -lgfortran
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
skipping incompatible C:/MinGW/lib/gcc/mingw32/6.3.0\libgfortran.a
when searching for -lgfortran
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
skipping incompatible C:/MinGW/lib/gcc/mingw32/6.3.0/libgfortran.dll.a
when searching for -lgfortran
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
skipping incompatible C:/MinGW/lib/gcc/mingw32/6.3.0/libgfortran.a
when searching for -lgfortran
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
cannot find -lgfortran
My main question is: Are these errors due to a problem in my code or are they related to a problem with my environment?
This is what my code looks like:
main.cpp
#include <iostream>
extern double f_add(double *, double *, double *);
int main() {
double a = 1.;
double b = 2.;
double c;
f_add(&a, &b, &c);
std::cout << c << std::endl;
}
f_add.f
real function f_add(a, b, c)
real a,b,c
c = a+b
end
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(test_cpp)
set(CMAKE_CXX_STANDARD 14)
SET (CMAKE_Fortran_COMPILER gfortran)
ENABLE_LANGUAGE(Fortran)
set(SOURCE_FILES
main.cpp
f_add.f
)
add_executable(test_cpp ${SOURCE_FILES})

I think your C++ code is missing extern "C" and some additional corrections to the Fortran code. For example, the following would work:
#include <iostream>
extern "C" {
double f_add(double, double);
}
int main() {
double a = 1.;
double b = 2.;
double c;
c = f_add(a, b);
std::cout << c << std::endl;
}
and,
function f_add(a, b) result(c) bind(C, name = "f_add")
use iso_c_binding, only: c_double
implicit none ! remove this line if your F77 code has implicitly-declared variables.
real(c_double), intent(in), value :: a, b
real(c_double) :: c
c = a + b
end function f_add
Then compile, link, and run (via MinGW GNU 10.1 that I am using),
gfortran -c f_add.f90
g++ -c main.cpp
g++ *.o -o main.exe
./main.exe
The output is,
3
I do not have CMake installed in MinGW, but setting it up should be straightforward with the above modifications. Your CMake file is fully functional in a Linux environment if that helps.

Related

LAPACKE C++ linking error. Unable to find function

I'm looking to use the LAPACKE library to make C/C++ calls to the LAPACK library. On multiple devices, I have tried to compile a simple program, but it appears LAPACKE is not linking correctly.
Here is my code, slightly modified from this example:
#include <cstdio>
extern "C"
{
#include "lapacke.h"
}
// extern "C" {
// // LU decomoposition of a general matrix
// void LAPACK_dgetrf(int* M, int *N, double* A, int* lda, int* IPIV, int* INFO);
// // generate inverse of a matrix given its LU decomposition
// void LAPACK_dgetri(int* N, double* A, int* lda, int* IPIV, double* WORK, int* lwork, int* INFO);
// }
extern "C"
{
#include <lapacke.h>
}
void inverse(double* A, int N)
{
int *IPIV = new int[N];
int LWORK = N*N;
double *WORK = new double[LWORK];
int INFO;
dgetrf_(&N,&N,A,&N,IPIV,&INFO);
dgetri_(&N,A,&N,IPIV,WORK,&LWORK,&INFO);
delete[] IPIV;
delete[] WORK;
}
int main(){
double A [2*2] = {
1,2,
3,4
};
inverse(A, 2);
printf("%f %f\n", A[0], A[1]);
printf("%f %f\n", A[2], A[3]);
return 0;
}
I have installed blas, lapack and lapacke using the apt package manager:
sudo apt-get install libblas-dev liblapack-dev liblapacke-dev
I am compiling with:
g++ -lblas -llapack -llapacke -I /usr/include main.cpp
the -I shouldn't be neccisary as far as I know, but I put it in anyway to be sure. I get the following error:
'/usr/bin/ld: /tmp/ccJSTpTf.o: in function `inverse(double*, int)':
main.cpp:(.text+0x9f): undefined reference to `dgetrf_'
/usr/bin/ld: main.cpp:(.text+0xc8): undefined reference to `dgetri_'
collect2: error: ld returned 1 exit status
This problem persists regardless if I compile lapacke from source, import the entire header, declare the functions themselves as extern, or change the names of the function to any of the following: dgetrf(), dgetrf_(), or LAPACK_dgetrf(), all common names I have seen for LAPACK-sytle calls.
Looking through the Lapacke header, it looks like LAPACK_dgetrf() translates to a lapack function call dgetrf_(). Unwrapping the static archives to look for dgetrf, I can find that liblapack.a has a function object dgetrf.o, and liblapacke.a has a function lapack_dgetrf.o. I'm thinking the root of the problem is that LAPACK_dgetrf() is basically dgetrf_(), and the linker cannot find dgetrf_(), because LAPACK has the function called dgetrf(), not dgetrf_(). I'm very confused though, as the compiler did not complain that any of the linked libraries didn't exist, and nobody else seems to have this issue on their devices, while I've had it now on 3 separate ones (all ubuntu based).
What do I need to do to get LAPACKE to compile, not just for C, but for C++?
I am compiling with: g++ -lblas -llapack -llapacke -I /usr/include main.cpp
That command line is wrong. Do this instead:
g++ main.cpp -llapacke -llapack -lblas
To understand why the order of sources and libraries matters, read this.

Solaris' stdlib.h functions implemented by C++ libs?

I am struggling with a port of an open-source tool to Solaris. Most things work, cmake/pkg-config/etc. are there, dependencies are found, gmake works, compiler and linker calls look all fine and tren, boom:
Undefined first referenced
symbol in file
std::qsort(void*, unsigned int, unsigned int, int (*)(const void*, const void*)) ...
This part I don't understand. At the first glance, std::qsort does not make sense, it is supposed to be part of C library, not STL. So I looked into stdlib.h and found a list of things like using std::sort; and dozens of other standard functions like free, malloc, atoi, etc., redirected in case of C++ context.
What is Oracle doing there and why? Which library am I supposed to link with if they do redirect it like this? Or why does CC command not pull that in automatically like GCC does?
I tried adding -lstdc++ but no luck.
Alternatively, the plain libc versions seem to be defined in <iso/stdlib_c99.h> (or <iso/stdlib_iso.h>). Is it legal to include one of those headers directly or will this wreak other random havoc at link time?
Edit:
since there are multiple suspicions of the build system weirdness, here is the basically the linking call from the gmake execution:
/opt/developerstudio12.6/bin/CC -std=c++11 -xO3 -DNDEBUG <i.e. bunch of object files> -o ../systest -L/opt/csw/lib/64 -lintl
I cannot see anything special there and I expect CC to figure out what to link to get the obligatory functionality.
The rule is that #include <xxx.h> puts names into the global namespace and is allowed to also put them in std. Conversely, #include <cxxx> puts names into std and is allowed to also put them into the global namespace. In practice, this means that there are two approaches to implementing the functions from the standard C library in C++: declare the standard C library names in the <xxx.h> headers and hoist those declarations into std in the cxxx headers, or declare the names in std in the headers and hoist those declarations into the global namespace in the <xxx.h> headers. With the former approach, the name of the function will be qsort; with the latter, it will be std::qsort. Either way, that error message usually indicates a setup problem with the compiler. The linker isn’t finding the standard library.
This compile command
/opt/developerstudio12.6/bin/CC -std=c++11 -xO3 -DNDEBUG ...
will produce a 32-bit executable. Per the Oracle CC man page:
On Oracle Solaris, -m32 is the default. On Linux systems supporting 64-bit programs, -m64 -xarch=sse2 is the default.
But this library option
-L/opt/csw/lib/64
is searching a directory full of 64-bit libraries.
Either add -m64 to the compile command or use the 32-bit library path.
Update
The question almost certainly would be answerable had it included the full error message, which is almost certainly something like this:
CC -g qsort.cc -o qsort
"qsort.cc", line 15: Error: Could not find a match for std::qsort(int[4], unsigned, unsigned, int(void*,void*)) needed in main(int, char**).
"/usr/include/iso/stdlib_iso.h", line 184: Note: Candidate 'std::qsort(void*, unsigned, unsigned, extern "C" int(*)(const void*,const void*))' is not viable: argument '4' can't be converted from 'int(void*,void*)' to 'extern "C" int(*)(const void*,const void*)'.
"/usr/include/iso/stdlib_iso.h", line 187: Note: Candidate 'std::qsort(void*, unsigned, unsigned, int(*)(const void*,const void*))' is not viable: argument '4' can't be converted from 'int(void*,void*)' to 'int(*)(const void*,const void*)'.
This code works just fine when compiled with Oracle Developer Studio 12.6 on Solaris 11.4:
#include <stdlib.h>
int compare( const void *p1, const void *p2 )
{
int i1 = *( ( int * ) p1 );
int i2 = *( ( int * ) p2 );
return( i1 - i2 );
}
int main( int argc, char **argv )
{
int array[ 4 ] = { 5, 8, 12, 4 };
qsort( array, sizeof( array ) / sizeof( array[ 0 ] ),
sizeof( array[ 0 ] ), &compare );
}

How to run a Fortran program within GNU Octave?

I want to run a Fortran program within Octave. I would like to do this for automation purposes and use Octave for all the data processing.
Is it possible to run a Fortran program from octave using cygwin, if so, could you provide me some pointers along that direction?
Moreover, I have a gfortran compiler installed in my system, Is there a way I could make use of it to complete my task mentioned above?
Furthermore, I tried to use mex to perform the same:
mckoctfile --mex HelloWorld.f
I got the following error after trying the mex approach:
c:/octave/octave~1.0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\rajan\AppData\Local\Temp/oct-qur1RF.o: in function `hi': C:\Tech Stuff\Fortran Programs/HelloWorld.f:3: undefined reference to `_gfortran_st_write'
c:/octave/octave~1.0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Tech Stuff\Fortran Programs/HelloWorld.f:3: undefined reference to `_gfortran_transfer_character_write'
c:/octave/octave~1.0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Tech Stuff\Fortran Programs/HelloWorld.f:3: undefined reference to `_gfortran_st_write_done'
c:/octave/octave~1.0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\rajan\AppData\Local\Temp/oct-qur1RF.o: in function `main':C:\Tech Stuff\Fortran Programs/HelloWorld.f:6: undefined reference to `_gfortran_set_args'
c:/octave/octave~1.0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Tech Stuff\Fortran Programs/HelloWorld.f:6: undefined reference to `_gfortran_set_options'
collect2.exe: error: ld returned 1 exit status
warning: mkoctfile: building exited with failure sta
How do I resolve this error to move forward?
Obviously your particular use-case may be a lot more complex than this, but here's a simple example to get you started (or to help you decide whether it's worth going down that route at all...)
Let's start with a simple octfile which performs simple integer addition, no fortran involved for now.
// in: simple_addition.cpp
#include <octave/oct.h>
DEFUN_DLD (simple_addition, args, ,"Add two integers via C++")
{
octave_value retval = args(0).int_value() + args(1).int_value();
return retval;
}
Compile:
mkoctfile -c simple_addition.cpp # compiles a simple_addition.o file
mkoctfile -o simple_addition simple_addition.o # links .o file to named output file
In octave:
octave:1> simple_addition(1,2)
ans = 3
Now let's put this aside for a minute, and see how we might call a fortran-defined function from pure c++. First let's create a simple integer addition function:
! in fortran_addition.f90
function fortran_addition(a,b) result(Out)
integer, intent(in) :: a,b ! input
integer :: Out ! output
Out = a + b
end function fortran_addition
and compile it using gfortran:
gfortran -c fortran_addition.f90 # creates fortran_addition.o
You can see (e.g. using nm fortran_addition.o) that the generated object contains a reference to a symbol under the name fortran_addition_ (note the added underscore at the end).
Now let's create a normal (i.e. non-octave-related) c++ wrapper program which calls the function defined via this symbol:
// in generic_fortran_addition_wrapper.cpp
#include <iostream>
extern "C" { int fortran_addition_( int *, int * ); }
int main() {
int a = 1, b = 2, fortran_result;
fortran_result = fortran_addition_( &a, &b );
std::cout << a << " + " << b << " = " << fortran_result << std::endl;
}
compile:
g++ -c generic_fortran_addition_wrapper.cpp
g++ -o addints generic_fortran_addition_wrapper.o fortran_addition.o
./addints # outputs `1 + 2 = 3` on the terminal
Now we have all the ingredients to create an octfile that wraps a fortran function:
// in fortran_addition_wrapper.cpp
#include <octave/oct.h>
extern "C" { int fortran_addition_( int *, int *); }
DEFUN_DLD (fortran_addition_wrapper, args, ,"Add two integers via fortran")
{
int a, b, fortran_result;
a = args(0).int_value();
b = args(1).int_value();
fortran_result = fortran_addition_( &a, &b );
octave_value retval(fortran_result);
return retval;
}
compile with mkoctfile:
mkoctfile -c fortran_addition_wrapper.cpp
mkoctfile -o fortran_addition_wrapper fortran_addition_wrapper.o fortran_addition.o
and then in octave:
octave:1> fortran_addition_wrapper(1,2)
ans = 3
Having said all this, obviously if you have a fully defined fortran program, rather than just linkable functions, and you have a running compiled executable on your system, then you can skip all the above 'formalities' and just call your executable via the system() command from octave. Obviously in this scenario it's up to you to pass the data in an octave-agnostic way ... but presumably if you have a standalone fortran executable, then presumably it already has a way of reading input data from the operating system.
EDIT as per the comments below, I've been reminded that I got side-tracked and answered the question that was asked in the comments to the original question, and forgot to address the error messages in the original question. As mentioned in my comment there, mkoctave is a generic wrapper to the gnu compiler collection. Those messages do not sound specific to octave, but rather that the compiler/linker complains that you're missing the fortran runtime libraries that define these basic functions.

Why do I get a segfault when calling my C++ function with .Call rather than .C?

My end goal is to call some C++ functions from within R, here is a MNWE of where I'm hitting a roadblock. If I'm reading the room correctly, I seem to get a segfault when I call my function with .Call but when I call it with .C everything works fine.
Here is my short C++ function
// test.cpp
#include <iostream>
extern "C" void fnTest() {
std::cout << "Hello" << std::endl;
}
Which I then compiled with
R CMD SHLIB -o test.so test.cpp
Which gave the following output:
g++ -std=gnu++11 -shared -L/usr/lib64/R/lib -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -o te
st.so test.o -L/usr/lib64/R/lib -lR
Now within R I did
> dyn.load("test.so")
> .C("fnTest")
Hello
list()
> .Call("fnTest")
Hello
*** caught segfault ***
address 0x30, cause 'memory not mapped'
Possible actions:
1: abort (with core dump, if enabled)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection:
The documentation that I read for these two functions is here and didn't seem to indicate much of a difference in the calling format of the two functions.
I tried several other variations (e.g. I was was able to pass arguments successfully to .C but not .Call) and didn't have any success.
What is the proper way to .Call a C++ function from within R?
Some notes on my eventual use case beyond this minimal example, hopefully this is not an XY problem:
I have a project with many complicated dependencies which I know how to build with CMake but not directly from g++. I was able to build a shared library from this project that I could then link into an "R compatible" shared library (R CMD SHLIB -o test.so test.cpp -L/path/to/my/lib/ -l my_lib_name) which I was able to dyn.load() into my R environment. At that point I then ran into the above .C vs. .Call issue.
From reading some additional documentation (that I should have found on the first pass), I believe that you cannot .Call a function that has a return type of void.
I could not find an explicit mention of this, but no example in the documentation (e.g. this section) listed a return type other than SEXP and at one point the documentation states that:
All the R objects you will deal with will be handled with the type SEXP
On the other hand, as documented in the Interface functions .C and .Fortran section, any function that you .C must have a return type of void:
Note that the compiled code should not return anything except through its arguments: C functions should be of type void and Fortran subprograms should be subroutines.
Here are some examples that can be compiled as in the OP. It didn't seem like there was a default "null" return type for .Call'ed functions, but allocVector(REALSXP, 0) R_NilValue seemed to work well.
// test.cpp
#include <R.h>
#include <Rinternals.h>
extern "C" void fnPrintC() {
Rprintf("Hello world!\n");
}
extern "C" SEXP fnPrintCall() {
Rprintf("Hello world!\n");
// return allocVector(REALSXP, 0);
return R_NilValue;
}
extern "C" SEXP fnAddCall(SEXP a, SEXP b) {
double* xa = REAL(a);
double* xb = REAL(b);
SEXP ans = allocVector(REALSXP, 2);
REAL(ans)[0] = *xa + *xb;
REAL(ans)[1] = *xa - *xb;
return ans;
}
Here they are called from R. Note we can send the (void) output to the dummy variable x if we don't want to see it.
> dyn.load("test.so")
> x <- .C("fnPrintC")
> Hello world!
> x <- .Call("fnPrintCall")
> Hello world!
> .Call("fnAddCall", 4, 3)
> [1] 7 1
In general, the documentation linked above was pretty helpful, I recommend starting there for anyone with a similar question, I certainly wish I'd read it more thoroughly earlier on.

Undefined reference to Fortran function in C++

I can't seem to figure out why this isn't working.
/* main.cpp */
#include <stdio.h>
extern "C"
{
int __stdcall inhalf(int *);
}
int main()
{
int toHalf = 2;
int halved = inhalf(&toHalf);
printf("Half of 2 is %d", halved);
return 0;
}
Ok, that looks good.
$ g++ -c main.cpp
No errors.
! functions.f90
function inhalf(i) result(j)
integer, intent(in) :: i
integer :: j
j = i/2
end function inhalf
I'm pretty sure that's right.
$ gfortran -c functions.f90
So far so good...
$ gcc -o testo main.o functions.o
main.o:main.cpp:(.text+0x24): undefined reference to `inhalf#4'
collect2.exe: error: ld returned 1 exit status
I've been looking this up for over an hour, but I couldn't find anything that worked for this case. How should I solve this?
For full C compatibility you can use the bind feature of modern Fortran:
! functions.f90
function inhalf(i) result(j) bind(C,name='inhalf')
integer, intent(in) :: i
integer :: j
j = i/2
end function inhalf
This allows you to give a name to the function that you can use in C (and others) without relying on the naming scheme your compiler uses on its own.
The __stdcall is Win32 only (and the default behavior for linking, see here). You can safely remove it. [Actually it is required for compiling your code in Linux. ]