3

The usage of default arguments for pointers in C++ can be demonstrated with following code

#include <iostream>

void myfunc_(int var, double* arr = 0) {
  if (arr == 0) {
    std::cout << "1 arg" << std::endl;
  } else {
    std::cout << "2 arg" << std::endl;
  }
}

int main() {
  myfunc(1);
  double *arr = new double[2];
  myfunc(1, arr);
}

in this case, the output of the program is

1 arg
2 arg

on the other hand, if i try to pass optional arguments from Fortran to C++, it does not work. The following example code demonstrates the sutiation

The myfunc.cpp

#include <iostream>

extern "C" void myfunc_(int var, double* arr = 0) {
  if (arr == 0) {
    std::cout << "1 arg" << std::endl;
  } else {
    std::cout << "2 arg" << std::endl;
  }
} 

and the Fortran main program

program main
use iso_c_binding

real*8 :: arr(2)

call myfunc(1)
call myfunc(1, arr)
end program main

and the mixed code (Fortran and C++) can be compiled using following command without any error

g++ -c myfunc.cpp
gfortran -o main.x myfunc.o main.f90 -lstdc++ -lc++ 

but the program prints

2 arg
2 arg

in this case. So, Is there any solution for this problem? Am i missing something in here? I think that using default arguments in mixed programming is not working as it expected but i need suggestion at this point.

epsilon
  • 33
  • 4
  • Although the answer to the dupe is not accepted, I think it addresses your issue, in other words, you cannot have non-C features (like default parameters) when compiling/linking with `extern C`. Here are some ways of simulating it: http://stackoverflow.com/q/2988038/3093378 – vsoftco Apr 06 '16 at 17:52
  • 1
    Using extern "C" will allow the linker bind fortran's myfunc(1) call to the function with 2 arguments. But only 1 is supplied, big time UB. You cannot do this, use a function with an other name like myfunc2(). – Hans Passant Apr 06 '16 at 17:56
  • 1
    @vsoftco I do not agree with duplicating this. The problem can be solved, just the optional argument part must be done in Fortran, it has support for this. – Vladimir F Героям слава Apr 06 '16 at 19:45
  • Thanks, I reopened it so feel free to add an answer. – vsoftco Apr 06 '16 at 22:02

1 Answers1

2

As pointed out in the comments, you cannot have default values of parameters in extern "C" functions. However, you can have optional arguments in the Fortran interface of the function and call it exactly the way you want:

#include <iostream>
extern "C" void myfunc(int var, double* arr) { //removed the = 0 and renamed to myfunc
  if (arr == 0) {
    std::cout << "1 arg" << std::endl;
  } else {
    std::cout << "2 arg" << std::endl;
  }
}

In Fortran create an interface describing the C(C++) function:

program main
use iso_c_binding

interface
  subroutine myfunc(var, arr) bind(C, name="myfunc")  
    use iso_c_binding
    integer(c_int), value :: var
    real(c_double), optional :: arr(*)
  end subroutine
end interface

!C_double comes from iso_c_binding
real(c_double):: arr(2)

call myfunc(1_c_int)
call myfunc(1_c_int, arr)
end program main

The key thing is the optional attribute in the interface. If you don't supply the optional array, a null pointer will be passed instead.

Also notice the value attribute to the var argument. It is necessary because the C++ function accepts the parameter by value.

Note: this is quite a recent addition as a TS to the Fortran 2008 standard, but is widely supported.

  • thanks for your solution. is this supported in g++ and gfortran? did you tested it? i am asking because when i try to compile your code with the commands given in my initial post, it complains about _myfunc function (Undefined symbols for architecture ...) and did not compile. – epsilon Apr 08 '16 at 06:33
  • @epsilon Yes it will work. Noice that I renamed _myfunc to myfunc and that I use `bind(C,name="myfunc")` Be sure to follow these details or leave _myfunc and use `bind(C,name="_myfunc")`. – Vladimir F Героям слава Apr 08 '16 at 07:16
  • Yes i know that the function name changed. It is weird and i am using your code without any change but it does not compile. I did not change anything in the function and the main program. do you think that there could be a issue of compiler version. – epsilon Apr 08 '16 at 11:15
  • BTW, i am using Mac OS along with GNU compiler and i think that the issue could be related with the OS and compilers. So, i tested the code under CentOS (gcc version 4.4.7 20120313) and it gives following error, when i try to compile it `gfortran -o main.x main.f90 myfunc.o main.f90:5.34: subroutine myfunc(var, arr) bind(C, name="myfunc") 1 Error: Variable 'arr' at (1) cannot have the OPTIONAL attribute because procedure 'myfunc' is BIND(C)`. With Intel compiler the error is `main.f90:(.text+0x49): undefined reference to `myfunc'`. – epsilon Apr 08 '16 at 11:16
  • 1
    @epsilon GCC 4..4 is too old. But I just checked the exact above code with GCC 4.8 (note I changed 2 lines in the C++) and it works. I compile as `gfortran opt.cc opt.f90 -lstdc++`. Maybe you call `_myfunc` somewhere else? – Vladimir F Героям слава Apr 08 '16 at 11:32
  • Okay. Thanks for your help. Now it is working. I was adding `#include ` but not `extern "C"`. Now, it compels and works without any problem. I think that missing `extern "C"` was causing the problem. Anyway, thanks for your help. – epsilon Apr 08 '16 at 11:53