Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Questions about calling Fortran from C++: string passing and others

564 views
Skip to first unread message

Haodong Du

unread,
May 13, 2021, 3:24:09 PM5/13/21
to
Hello All,

I'm having some difficulty calling Fortran from C++ side. I realized that the ideal way is using the C binding from Fortran side. But since I don't have the Fortran source code, that's not possible. This is the interface I have now

main.cpp
void fortfunc_(int *ii, double *ff, double aa[][3], char ss[], int ll);

func.f90
subroutine fortfunc(ii,ff,aa,ss)
integer :: ii
real*8 :: ff
real*8, dimension(3,3) :: aa
character(len = 10) :: s

Most parts is working as expected except for the string/char array passing. I'm passing an additional length number as suggested by some online sources. When I tried to write the string in Fortran, it's not terminated as I expected. Also in the end, I run into a "*** stack smashing detected ***: terminated". It feels like the length number does not do anything and it's not really needed.

The complete code:
main.cpp
#include <iostream>
#include <cstring>

using namespace std;

extern "C" {
void fortfunc_(int *ii, double *ff, double aa[][3], char ss[], int ll);
}

int main()
{

int ii=1;
double ff=2.17;
double aa[3][3] = {1,2,3,4,5,6,7,8,9};
char ss[] = "hello";
int ll = strlen(ss);

cout << "original in C++" << endl;
cout << ii << " "<< ff << endl;
cout << ss << endl;

for(size_t n=0; n<3; n++) {
for (size_t m=0; m<3; m++) {
cout << aa[n][m];
}
}

fortfunc_(&ii,&ff,aa,ss,ll);

cout << "passed back to C++"
cout << ii << " "<< ff << endl;
cout << ss << endl;

for(size_t n=0; n<3; n++) {
for (size_t m=0; m<3; m++) {
cout << aa[n][m] << " ";
}
cout << endl;
}
return 0;
}

func.f90
subroutine fortfunc(ii,ff,aa,ss)
integer :: ii
real*8 :: ff
real*8, dimension(3,3) :: aa
character(len = 10) :: ss

write(*,*) "Passed to Fortran"
write(*,*) ss
write(*,*) ii, ff
write(*,*) aa

ii = 2
ff = 3.1415
aa = reshape((/1,2,3,4,5,6,7,8,9/),shape(aa))
ss = "there" // CHAR(0)

return
end

The output:
original in C++
1 2.17
hello
1 2 3
4 5 6
7 8 9
Passed to Fortran
1 2.1699999999999999
1.0000000000000000 2.0000000000000000 3.0000000000000000 4.0000000000000000 5.0000000000000000 6.0000000000000000 7.0000000000000000 8.0000000000000000 9.0000000000000000
hello)B
passed back to C++
2 3.1415
there
1 2 3
4 5 6
7 8 9
*** stack smashing detected ***: terminated
zsh: abort ./main

So the string passing from C++ to Fortran is not working right. I also got a stack issue after all steps are finished. After reading a reddit post, i added -fno-stack-protector compiler flags. Everything works correctly, including the string passing. But this feels like magic. I still want to know what is the correct way to call fortran from c++ without using the standard c binding.

Thank you,
Haodong Du




Gary Scott

unread,
May 13, 2021, 6:30:47 PM5/13/21
to
Passing the string length correctly is critical. Some compilers place
it immediately after the character variable, some place it at the end of
the argument list. Some specific fortran procedures may work ok with a
c null terminator, but that's very procedure specific. If you could
find which compiler the fortran procedure was compiled with, that could
provide a clue as to how to handle the string length.

>
>
>

gah4

unread,
May 13, 2021, 8:39:09 PM5/13/21
to
On Thursday, May 13, 2021 at 12:24:09 PM UTC-7, duhd...@gmail.com wrote:

> I'm having some difficulty calling Fortran from C++ side. I realized that the ideal way is
> using the C binding from Fortran side. But since I don't have the Fortran source code,
> that's not possible. This is the interface I have now
>
> main.cpp
> void fortfunc_(int *ii, double *ff, double aa[][3], char ss[], int ll);
>
> func.f90
> subroutine fortfunc(ii,ff,aa,ss)
> integer :: ii
> real*8 :: ff
> real*8, dimension(3,3) :: aa
> character(len = 10) :: s
>
> Most parts is working as expected except for the string/char array passing.
> I'm passing an additional length number as suggested by some online sources.
> When I tried to write the string in Fortran, it's not terminated as I expected.
> Also in the end, I run into a "*** stack smashing detected ***: terminated".
> It feels like the length number does not do anything and it's not really needed.

Without knowing any actual compiler calling sequences, a fixed length character
string doesn't need a passed length. It would be for (len=*), though.

The stack smash message sounds like the stack is being mess up.

Assuming that this is x86 based, there have been a few different calling
conventions over the years. Some Fortran compilers use(d) STDCALL,
in which case the called routine pops the arguments off the stack.

Because C allows for a variable number of arguments, it can't in
general use STDCALL, and commonly uses CDECL.

As well as I know, most recent Fortran compilers use CDECL, and even
some not so recent ones. However, an extra argument normally causes
no problems with CDECL, so the message is suspicious.

If you know which compiler it is, it should be possible to find out
its calling conventions. If not, you are pretty stuck, even if calling
it from Fortran.

FortranFan

unread,
May 13, 2021, 10:16:35 PM5/13/21
to
On Thursday, May 13, 2021 at 3:24:09 PM UTC-4, duhd...@gmail.com wrote:
> .. I still want to know what is the correct way to call fortran from c++ without using the standard c binding.
> ..

@Haodong Du,

Without the "standard c binding", note there is no such thing as a correct way. You may find some ways that work with different compiler options and/or compilers on specific platforms, each one of which will be "magic" and none of which are likely to be portable.

Your best bet is be to either to use Fortran's standard features on the Fortran side of the code and/or to use a Fortran companion processor along with "ISO_Fortran_binding.h" header in the "extern C" attributed sections of your C++ code to interoperate with Fortran.

Haodong Du

unread,
May 13, 2021, 10:42:40 PM5/13/21
to
My bad for not mentioning the compiler I used. It's gcc/gfortran 9.30 which comes with the Ubuntu 20.04 x64. I think gcc is probably the most commonly used compiler, so people should have a way somehow.
The length argument is both behind the string and the last in the argument list. So that's not an issue.
I also tried without the length parameter. The errors are the same, which means the length parameter does not work at all.
@FortranFan, could you elaborate the latter option?

gah4

unread,
May 14, 2021, 5:55:23 AM5/14/21
to
On Thursday, May 13, 2021 at 7:42:40 PM UTC-7, duhd...@gmail.com wrote:

(snip)

> My bad for not mentioning the compiler I used. It's gcc/gfortran 9.30 which comes with the
> Ubuntu 20.04 x64. I think gcc is probably the most commonly used compiler,
> so people should have a way somehow.
> The length argument is both behind the string and the last in the argument list.
> So that's not an issue.
> I also tried without the length parameter.
> The errors are the same, which means the length parameter does not work at all.

Well, the right way to do it is to make a wrapper. Write a subroutine that uses BIND(C)
for its own arguments, and then calls the needed Fortran routine in the usual way.

For that to work, you need the matching compiler to the called routine, which you didn't
indicate in the first post. Pretty much, you should always use a matching compiler.

There aren't quite as many different ways to call functions in C, so sometimes
you can get away with it in C, but less often in Fortran. So, just to be sure, the routine
you are calling was also compiled with the mentioned compiler.

Some is described here:

https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html




Cyrmag

unread,
May 14, 2021, 6:07:41 AM5/14/21
to
On 5/13/2021 2:24 PM, Haodong Du wrote:
Haodong Du:

You are giving the Fortran compiler incorrect information by telling it
that the character argument is 10 in length. The statement

ss = "there" // CHAR(0)

will cause ss to be filled with the 5+1 characters on the right, PLUS 4
space characters. That's where the stack molestation may occur, because
the actual argument, passed from C/C++, is only 6 characters long. The
additional 4 spaces overwrite whatever follows ss in memory in the C++
caller.

Bugs such as this may go undetected for years if they do not cause
observable misbehavior. Tools such as Valgrind may help detect such errors.

-- CyrMag

Edmondo Giovannozzi

unread,
May 14, 2021, 6:15:49 AM5/14/21
to
The easiest way, if you have access to the Fortran compiler, is to write a small Fortran routine with the bind(C) attribute, so that it could be called by C routines.
It will then call the old Fortran routine.

I have used this method many times.

gah4

unread,
May 14, 2021, 6:17:46 AM5/14/21
to

OK, I didn't read the code carefully enough.

You have to pass a 10 character char array.

If you call this routine from Fortran it has to be CHARACTER*10,
it can't be, for example CHARACTER*5.

C works just fine with fixed length strings that are not null terminated.

If you make it:

char ss[10] = "hello ";

then it will agree with what Fortran expects.

gah4

unread,
May 14, 2021, 6:25:15 AM5/14/21
to
On Friday, May 14, 2021 at 3:07:41 AM UTC-7, CyrMag wrote:

(snip)

> You are giving the Fortran compiler incorrect information by telling it
> that the character argument is 10 in length. The statement
> ss = "there" // CHAR(0)
> will cause ss to be filled with the 5+1 characters on the right, PLUS 4
> space characters. That's where the stack molestation may occur, because
> the actual argument, passed from C/C++, is only 6 characters long. The
> additional 4 spaces overwrite whatever follows ss in memory in the C++
> caller.

Yes.

More obvious to me, is to treat the string in C like a Fortran string.
Blank padded to the actual length. Declaring:

char ss[10]="there ";
will pad with spaces. C will figure out that there is no space for a '\n'
and so not put one in. Then, as you note, Fortran will put 10 characters in.

The C language has no problem with strings that arent' null terminated.
Many library routines assume it, though, so you have to be careful with those.

When I read the first post, I thought you were calling a routine that you didn't
have source for, which complicates things.


Ev. Drikos

unread,
May 14, 2021, 9:14:58 AM5/14/21
to
On 14/05/2021 05:42, Haodong Du wrote:
> I also tried without the length parameter. The errors are the same, which means the length parameter does not work at all.

I think that the length parameter in gfortran works if the length is 1.
The issue has been discussed also in GCC Bugzilla. You may find it or
more luckily a maintainer may read this thread and guide you.

BTW, I tried this API which worked (no garbages after 'hello'):

subroutine fortfunc(ii,ff,aa,ss)
integer :: ii
real*8 :: ff
real*8, dimension(3,3) :: aa
character(len = *) :: ss


Haodong Du

unread,
May 14, 2021, 10:18:53 AM5/14/21
to
Thanks for all the help.
1. I don't have the FORTRAN library source code. But I know the interface. The example I gave is just a fake code mimicking the interface. The library is also compiled with gcc (not sure which version, but i can ask).
2. I would try writing a wrapper if necessary. Thanks for the suggestion.
3. character(len = *) not working correctly. It does not produce errors but string not printed.
4. Matching the length between C and Fortran is doing the work. I will go with this approach first.
5. I'm still wondering what the length parameter does since all references are mentioning it but it does not do anything for me.

Gary Scott

unread,
May 14, 2021, 11:14:55 AM5/14/21
to
Whether it does anything probably depends on what the Fortran procedure
is doing. Without the source, it's hard to tell.

FortranFan

unread,
May 14, 2021, 12:48:34 PM5/14/21
to
On Friday, May 14, 2021 at 10:18:53 AM UTC-4, duhd...@gmail.com wrote:

> ..
> 2. I would try writing a wrapper if necessary. Thanks for the suggestion.
> ..

@Haodong Du,

Can you please first share if plan to do any additional work using Fortran? If so, I strongly suggest you also use the Fortran Discourse site where it is much easier to engage with Fortranners in terms of markdown, file attachments, images, etc. in posts:
https://fortran-lang.discourse.group/

Secondly, if you have a Fortran procedure whose interface is known and toward which you are *willing* to write wrapper(s) using *modern* facilities to consume said procedure in different contexts, here is an illustration I would recommend using the example you have provided.:

--- begin Fortran code ---
subroutine fortfunc(ii,ff,aa,ss)
! Mimic the Fortran procedure here
integer :: ii
real*8 :: ff
real*8, dimension(3,3) :: aa
character(len=10) :: ss

write(*,*) "Passed to Fortran"
write(*,*) ss
write(*,*) ii, ff
write(*,*) aa

ii = 2
ff = 3.1415
aa = reshape((/1,2,3,4,5,6,7,8,9/),shape(aa))
ss = "there" // CHAR(0)

return
end
module wrap_func_m
! Wrapper for the Fortran procedure
use, intrinsic :: iso_c_binding, only : c_int, c_double, c_char
interface
subroutine fortfunc(ii,ff,aa,ss)
integer :: ii
real*8 :: ff
real*8, dimension(3,3) :: aa
character(len=10) :: ss
end subroutine
end interface
contains
subroutine wrap_func(ii, ff, aa, ss, irc) bind(C, name="wrap_func")
integer(c_int), intent(inout) :: ii
real(c_double), intent(inout) :: ff
real(c_double), intent(inout), target :: aa(:,:)
character(kind=c_char,len=*), intent(inout), target :: ss
integer(c_int), intent(inout), optional :: irc
! Error handing here; do the needful
if ( size(aa,dim=1) < 3 ) then
print *, "wrap_func: ", size(aa,dim=1)
if ( present(irc) ) irc = 1 ! or error stop if appropriate
return
end if
if ( len(ss) < 10 ) then
print *, "wrap_func: ", len(ss)
if ( present(irc) ) irc = 2 ! or error stop if appropriate
return
end if
block
real(c_double), pointer :: a(:,:)
character(kind=c_char,len=10), pointer :: s
a => aa(1:3,1:3)
s => ss
call fortfunc(ii,ff,aa,ss)
end block
end subroutine
end module
--- end Fortran code ---

--- begin C++ code ---
#include <iostream>
#include <cstring>
#include "ISO_Fortran_binding.h"

using namespace std;

extern "C" {
// Prototype for the Fortran procedure
void wrap_func(int *, double *, CFI_cdesc_t *, CFI_cdesc_t *, int *);
}

int main()
{

int ii=1;
double ff=2.17;
double aa[3][3] = {0,0,0,0,0,0,0,0,0};
char ss[] = "abcdefghij";
size_t ll = strlen(ss);

cout << "original in C++" << endl;
cout << ii << " "<< ff << endl;
cout << ss << endl;

for(size_t n=0; n<3; n++) {
for (size_t m=0; m<3; m++) {
cout << aa[n][m];
}
}
cout << endl;

int rc = 0;
// Use macro to set aside an address to "description" of array data of rank-2
CFI_CDESC_T(2) a;
CFI_index_t extents[2] = { 3, 3 };
// Initialize the C descriptor as a rank-2 objeect of doubles
rc = CFI_establish((CFI_cdesc_t *)&a, (void *)aa, CFI_attribute_pointer, CFI_type_double, sizeof(double),
(CFI_rank_t)2, extents);
if (rc != CFI_SUCCESS) return rc;
// Use macro to set aside an address to "description" of string data
CFI_CDESC_T(0) str;
// Initialize the C descriptor as a scalar character nonpointer data type
rc = CFI_establish((CFI_cdesc_t *)&str, (void *)ss, CFI_attribute_other, CFI_type_char, ll, 0, NULL);
if (rc != CFI_SUCCESS) return rc;

wrap_func(&ii, &ff, (CFI_cdesc_t*)&a, (CFI_cdesc_t*)&str, &rc);
if (rc != 0) return rc;

cout << "passed back to C++" << endl;
cout << ii << " "<< ff << endl;
cout << ss << endl;

for(size_t n=0; n<3; n++) {
for (size_t m=0; m<3; m++) {
cout << aa[n][m] << " ";
}
cout << endl;
}
return rc;
}
--- end C++ code ---

Program behavior:
--- begin console output ---

C:\Temp>ifort /c /standard-semantics func.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.2.0 Build 20210228_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.


C:\Temp>cl /c /W3 /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29337 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

main.cpp
C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\compiler\include\ISO_Fortran_binding.h(156): warning C4200: nonstandard extension used: zero-sized array in struct/union
C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\compiler\include\ISO_Fortran_binding.h(156): note: This member will be ignored by a defaulted constructor or copy/move assignment operator
main.cpp(41): warning C4200: nonstandard extension used: zero-sized array in struct/union
main.cpp(41): note: This member will be ignored by a defaulted constructor or copy/move assignment operator

C:\Temp>link main.obj func.obj /subsystem:console /out:main.exe
Microsoft (R) Incremental Linker Version 14.28.29337.0
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Temp>main.exe
original in C++
1 2.17
abcdefghij
000000000
Passed to Fortran
abcdefghij
1 2.17000000000000
0.00000000000000 0.00000000000000 0.00000000000000
0.00000000000000 0.00000000000000 0.00000000000000
0.00000000000000 0.00000000000000 0.00000000000000
passed back to C++
2 3.1415
there
1 2 3
4 5 6
7 8 9

C:\Temp>
--- end console output ---

A couple of notes:
1) gfortran has incomplete implementation of support toward Fortran 2018 that hopefully some volunteer can pickup and resolve
2) With above, you can use the string class in C++ and also possibly STL vector container for array data, greatly easing the consumption of Fortran code on the C++ side

gah4

unread,
May 14, 2021, 2:19:33 PM5/14/21
to
On Thursday, May 13, 2021 at 12:24:09 PM UTC-7, duhd...@gmail.com wrote:
> Hello All,
>
> I'm having some difficulty calling Fortran from C++ side. I realized that the
> ideal way is using the C binding from Fortran side. But since I don't have the
> Fortran source code, that's not possible. This is the interface I have now

(snip)

> void fortfunc_(int *ii, double *ff, double aa[][3], char ss[], int ll);

(snip)

> char ss[] = "hello";

To explain this a little more, either for the OP or others reading this,
in a C function declaration you can say:

void fortfunc_(int *ii, double *ff, double aa[][3], char ss[], int ll);

which declares ii, ff, aa, and ss as pointers, and specifically
ss is pointer to char. (and aa is actually double *aa[3], or if
precedence isn't so obvious, *(aa[3]).

On the other hand, and it might be that Fortran doesn't have this yet,

char ss[] = "hello";

is actually char ss[6]="hello"; or maybe more obviously

char ss[6] = { 'h', 'e', 'l', 'l', 'o', '\0'};

It declares an array of length 6 and initializes it. Specfically, it take the length
from the initializing data. As far as I know, Fortran still doesn't have the feature
to allow declaring arrays or strings based on the size of the initialization, or maybe
only recently added.

Actually, to match Fortran it should be:

static char ss[6] = "hello";

And note that it would fail the same way if you called if from a Fortran program with:

character*6 :: ss="hello" // char(0)

(But as noted, I would not use null terminated strings with Fortran.)

If you declare the C array:

static char ss[10]="hello ";

with five blanks, then it will (silently) ignore the trailing null that it would
otherwise include.

In C you could declare an int array:

static int x[] = { 1, 2, 3, 4, 5 };

which declares a five element array and initializes it.
(It works for auto arrays, too.)

The ability to allocate an array based on the initializers goes back pretty
far, as it was needed in Unix to initialize a structure array for the available
I/O devices. You can then find out the length of the array, such as the x array,
as (sizeof(x)/sizeof(*x)) slightly less convenient that the Fortran size function.








gah4

unread,
May 14, 2021, 2:42:25 PM5/14/21
to
On Friday, May 14, 2021 at 9:48:34 AM UTC-7, FortranFan wrote:

(snip)

> Secondly, if you have a Fortran procedure whose interface is known and toward which
> you are *willing* to write wrapper(s) using *modern* facilities to consume said
> procedure in different contexts, here is an illustration I would recommend using
> the example you have provided.:


> extern "C" {
> // Prototype for the Fortran procedure
> void wrap_func(int *, double *, CFI_cdesc_t *, CFI_cdesc_t *, int *);
> }

I suppose so, but the usual idea of wrappers is to use the conventions of the calling language.

That is, make it look like it would look if it was a called C function.

Since C doesn't have assumed shape, one passes the lengths as additional
arguments. There are, then, two ways to work with arrays of dimension more than one.

If all except the leftmost dimension is known, so [3] in the example, you can
declare them as above. Otherwise in C, when dynamically allocating arrays
of unknown size, it is more usual to allocate arrays of pointers.

For the OP case, *aa[3] doesn't seem so bad, but many C programs would use **aa.

In that case, the wrapper would allocate a Fortran allocatable array of the
right size, and copy the data over. It could than call Fortran as assumed shape.

If the Fortran program expects a fixed size array, as it looks like so far,
then not much need for a variable sized array wrapper.




FortranFan

unread,
May 14, 2021, 5:40:23 PM5/14/21
to
On Friday, May 14, 2021 at 2:42:25 PM UTC-4, gah4 wrote:

> ..
> I suppose so, but the usual idea of wrappers is to use the conventions of the calling language.
>
> That is, make it look like it would look if it was a called C function.
> ..
> If the Fortran program expects a fixed size array, as it looks like so far,
> then not much need for a variable sized array wrapper.


The only language binding offered by the Fortran standard is with a companion C processor. And it is as if called as a C function compatible with said companion C processor that can consume Fortran from C++, etc.

Explicit shape *dummy* array arguments and explicit-length CHARACTER *dummy* scalar declarations are practically dangerous, they cause many problems for most folks. And the standard and the compilers are not of much help here. That is why in the wrapper procedure I show upthread I use assume-shape array dummy and deferred-length character scalar. The only real stipulation of the kind of a subprogram in Fortran shown by OP is that the actual argument be of matching or greater shape and the scaler be of same or longer length. The wrapper can provide facility to protect against this, as shown above.

gah4

unread,
May 14, 2021, 7:13:32 PM5/14/21
to
On Friday, May 14, 2021 at 2:40:23 PM UTC-7, FortranFan wrote:

(I wrote)

> > I suppose so, but the usual idea of wrappers is to use the conventions of the calling language.
> >
> > That is, make it look like it would look if it was a called C function.

> > If the Fortran program expects a fixed size array, as it looks like so far,
> > then not much need for a variable sized array wrapper.

> The only language binding offered by the Fortran standard is with a companion
> C processor. And it is as if called as a C function compatible with said
> companion C processor that can consume Fortran from C++, etc.

C calling conventions are more standard than Fortran, but in all cases one
normally expects a companion or same processor. The change to 64 bit
code has resulted in some incompatibilities between C compilers.

> Explicit shape *dummy* array arguments and explicit-length
> CHARACTER *dummy* scalar declarations are practically dangerous,
> they cause many problems for most folks.

That have and still do for both Fortran and C. As C doesn't have anything
like assumed-shape, they might still be more of a problem in C.

But older Fortran programmers and most C programmers know about this,
and work to keep things right.

Note, for example, that in a 3D world a common array dimension is 3, and
a transformation matrix might be 3x3. That isn't likely to change soon.

> And the standard and the compilers are not of much help here.
> That is why in the wrapper procedure I show upthread
> I use assume-shape array dummy and deferred-length character scalar.

The are nice, and it is nice to see the addition to Fortran. Even so, I suspect
that no-one will use it for calling between C functions.

> The only real stipulation of the kind of a subprogram in Fortran
> shown by OP is that the actual argument be of matching or greater
> shape and the scaler be of same or longer length.
> The wrapper can provide facility to protect against this, as shown above.

It does, and it is nice to see it done. In the OP case, calling a Fortran program
that doesn't use those features, at least as far as we know, it seems
more than is needed.


Ev. Drikos

unread,
May 15, 2021, 12:24:39 AM5/15/21
to
On 14/05/2021 17:18, Haodong Du wrote:
> 3. character(len = *) not working correctly. It does not produce errors but string not printed.

Just tried it with your example in RHEL-7 also and worked as in MacOS.

gah4

unread,
May 15, 2021, 3:17:13 AM5/15/21
to
On Friday, May 14, 2021 at 7:18:53 AM UTC-7, duhd...@gmail.com wrote:

(snip)


> 5. I'm still wondering what the length parameter does since all references are mentioning it but it does not do anything for me.

When a string is passed to another routine, the dummy variable (in the called routine)
can be CHARACTER*(*) or CHARACTER*10 (or another number).

The called routine doesn't know, so passes the length.

The called routine can use or ignore the length.

In Fortran 66, before CHARACTER variables, character (Hollerith) constants
could be passed to variables of other type, commonly INTEGER.

Fortran 77 allows passing them to CHARACTER arguments that can use
the length, or others that can't. Putting them at the end of the argument
list works in both cases. (Except maybe with STDCALL.)

It might be that if you turn on bounds checking, that it checks using the length.
Message has been deleted

Haodong Du

unread,
May 19, 2021, 7:11:56 PM5/19/21
to
Thanks all. I've been digging around with all the suggestions. Now I know how char array should be passed. The best way seems to be padding it with space and don't use \0. the hidden length parameter seems useful when in Fortran you don't specify the length explicitly. https://stackoverflow.com/questions/30419704/fortran-pass-character81-array-to-c-c-code This post also helped me.

I actually need to deal with 2D array with variable length. Thanks @gah4 for explaining. But I'm still confused. Suppose a Fortran subroutine takes aa[n][m], where n and m are also to be passed to Fortran. How can I call it from C++?

P.S. I just accidentally posted from another email of mine. deleted. sorry for the confusion.

gah4

unread,
May 19, 2021, 8:31:54 PM5/19/21
to
On Wednesday, May 19, 2021 at 4:11:56 PM UTC-7, duhd...@gmail.com wrote:

(snip)
> I actually need to deal with 2D array with variable length. Thanks @gah4 for explaining.
> But I'm still confused.
> Suppose a Fortran subroutine takes aa[n][m], where n and m are also
> to be passed to Fortran. How can I call it from C++?

In the case of a Fortran array aa(m,n) (using Fortran notation, not C notation)
the program pretty much expects a pointer to an array of m*n elements,
for either the explicit size or assumed size case.

(For assumed shape, it was described by someone else above.)

It does this so well, that it is legal according to the standard to pass
a 1D array with at least m*n elements to a subroutine expecting aa(m,n).
(Or just about any other combination you might think, such as passing the
2D array to a 1D dummy argument. As long as there are at least enough.)

Do note that Fortran indexes arrays with the leftmost subscript varying
fastest, while C does it the other way. (And C++, too.)

In the case of constant dimensions, you can dimension an array in C:

double x[3][4];

and call a Fortran program expecting:

double precision x(4,3)

You can also dimension the Fortran array:

double precision x(4,*)

since the rigthmost subscript is not needed for subscript calculation
(unless you have bounds-checking turned on).

That works the same as a C function declaring its argument:

int f(double x[][4]);

since as with Fortran, the leftmost is not needed for subscript calculation.

That is equivalent, and actually compiled as:

int f(double *x[4]);

Traditionally in C, arrays are declared with constant bounds.
As well as I know, this has been changing in recent versions of the
standard, in possibly strange and inconsistent ways.

Fortran, back to Fortran 66, allows for array dummy arguments to be
declared with variable bounds, usually passed as arguments:

subroutine g(x, m, n)
integer m, n
double precision x(m,n)

(Or dimension x(m,*), as n isn't used in subscript calcualtions.)

This is allowed only for arguments. There is no dynamic allocation, but this
accesses an existing, already allocated, array.

C doesn't have that convenience, but you can do it when you reference an array:

x[i*m+j]=0;

Or for C programmers who want to save typing:

#define X(i,j) x[i*m+j]

(which also uses the case sensitivity of names in C).

#define X(j,i) x[i*m+j]

for Fortran programmers.

In any case, all of these methods, in both C and Fortran, use a contiguous
array of elements, and divide them up appropriately.

I presume you also know that C indexes arrays starting from 0, as the calculation
is based on adding to a pointer. Fortran by default uses 1, and in some cases
only allows for the lower bound to be 1. (That is, in array expressions.)

C programs can dynamically allocate a 1D array, to be used by any of the above:

double *x;
x=malloc(m*n*sizeof(*x));

C programs can dynamically allocate 2D arrays with all except the leftmost
subscript constant:

double *x[3];
x=malloc(m*3*sizeof(*x));

As I noted previously, though, commonly when C programmers need a 2D array
of dynamically allocated both dimensions (or more than 2D), one allocates
an array of pointers.

double **y;
y=malloc(m*sizeof(*y));
for(int i=0; i<m; i++) y[i]=malloc(n*sizeof(**y));

Fortran does not have a convenient way to deal with such arrays,
or with arrays of pointers to arrays allocated in Fortran.

(Note that, that in any language, allocating this way allows for a ragged,
that is non-rectangular array.)





Haodong Du

unread,
May 19, 2021, 9:25:08 PM5/19/21
to
Thanks @gah4. You cleared a lot of my confusions. Actually you can declare an array in C with non-constant size. I read that was effective since C99.
This is what I find working in case someone needs it. in C++, declare double C[m][m], and declare it as a simple double *C in the interface, and call it by the address of the first element &(C[0][0]). Technically C and &(C[0][0]) should have the same value, but compilers would complain type issues. Based on your suggestion, maybe I can also allocate memory manually or declare it as a 1D array and use it as 2D in Fortran.
This is a little awkward in my opinion, but it works. Appreciate if there are better options

gah4

unread,
May 20, 2021, 1:14:04 AM5/20/21
to
On Wednesday, May 19, 2021 at 6:25:08 PM UTC-7, duhd...@gmail.com wrote:

(snip, I wrote)

> > Traditionally in C, arrays are declared with constant bounds.
> > As well as I know, this has been changing in recent versions of the
> > standard, in possibly strange and inconsistent ways.

(snip)

> Thanks @gah4. You cleared a lot of my confusions.
> Actually you can declare an array in C with non-constant size. I read that was effective since C99.

One that I saw said that it was added in one version, and removed in another.
But most C programmers are used to arrays of pointers, anyway.

But also note that there are two separate questions, and I didn't figure out which
versions of C did which. One is for the declaration (and allocation) of the original
array. The other is for (what Fortran calls) the dummy argument.

Fortran 66 added, and Fortran still allows, variables for dummy arguments.

Since C has always allowed for auto allocation, allocating variable sized
arrays is not hard, but C didn't allow it in either K&R version or ANSI 89 version.

The latter is just keeping track of bounds until the array reference.

> This is what I find working in case someone needs it. in C++, declare double C[m][m],
> and declare it as a simple double *C in the interface, and call it by the address of the
> first element &(C[0][0]). Technically C and &(C[0][0]) should have the same value,
> but compilers would complain type issues.

Yes, but you can always cast it to (double*) and that is what most would do.

Ron Shepard

unread,
May 20, 2021, 2:37:29 PM5/20/21
to
On 5/19/21 7:31 PM, gah4 wrote:
> As I noted previously, though, commonly when C programmers need a 2D array
> of dynamically allocated both dimensions (or more than 2D), one allocates
> an array of pointers.
>
> double **y;
> y=malloc(m*sizeof(*y));
> for(int i=0; i<m; i++) y[i]=malloc(n*sizeof(**y));
>
> Fortran does not have a convenient way to deal with such arrays,
> or with arrays of pointers to arrays allocated in Fortran.

I would say that you can do the same thing in fortran. You allocate an
array of derived type with an array pointer or an allocatable array
component. This requires doing separate allocate() for each member of
the array, the same as the above C code.

In both cases, you can also allocate/malloc a long 1D array, and then
assign pointers to the appropriate slices within the 1D array. This
approach has the advantage of minimizing the number of allocate/malloc
operations, it results in contiguous memory localization, thereby
allowing the entire array to be treated as a single entity, e.g. in i/o
statements, without needing to loop over the individual members.

This all works, in both languages, with either fixed-length members or
ragged length members. I guess one can argue this is more convenient in
C than in fortran, but the functionality, and probably even the
low-level compiled code, is the same in both cases when done with
pointers. But when done with fortran allocatable components rather than
pointers, then the fortran semantics allows nonalising assumptions to
apply, and the code can be tuned/optimized accordingly.

$.02 -Ron Shepard

gah4

unread,
May 20, 2021, 6:54:27 PM5/20/21
to
On Thursday, May 20, 2021 at 11:37:29 AM UTC-7, Ron Shepard wrote:
> On 5/19/21 7:31 PM, gah4 wrote:
> > As I noted previously, though, commonly when C programmers need a 2D array
> > of dynamically allocated both dimensions (or more than 2D), one allocates
> > an array of pointers.

> > double **y;
> > y=malloc(m*sizeof(*y));
> > for(int i=0; i<m; i++) y[i]=malloc(n*sizeof(**y));

> > Fortran does not have a convenient way to deal with such arrays,
> > or with arrays of pointers to arrays allocated in Fortran.

> I would say that you can do the same thing in fortran. You allocate an
> array of derived type with an array pointer or an allocatable array
> component. This requires doing separate allocate() for each member of
> the array, the same as the above C code.

Yes if you are doing it all in Fortran, it is only slightly less convenient.
Partly because you have to declare the structure, but mostly because
it is inconvenient to reference array elements. Though I suppose
as I noted previously, you can use the C preprocessor to fix that.

There is a COBOL feature adopted by PL/I that could fix that,
but Fortran doesn't have it yet.

> In both cases, you can also allocate/malloc a long 1D array, and then
> assign pointers to the appropriate slices within the 1D array. This
> approach has the advantage of minimizing the number of allocate/malloc
> operations, it results in contiguous memory localization, thereby
> allowing the entire array to be treated as a single entity, e.g. in i/o
> statements, without needing to loop over the individual members.

> This all works, in both languages, with either fixed-length members or
> ragged length members. I guess one can argue this is more convenient in
> C than in fortran, but the functionality, and probably even the
> low-level compiled code, is the same in both cases when done with
> pointers. But when done with fortran allocatable components rather than
> pointers, then the fortran semantics allows nonalising assumptions to
> apply, and the code can be tuned/optimized accordingly.

I suspect that Fortran pointers have more overhead, but maybe not
so much more.

But the inconvenience I meant above comes when a (double **) is
passed to Fortran. You then still need the structure of array
pointers to REAL(c_double), then loop over an array of
TYPE(c_ptr) and C_F_POINTER each one.

Not hugely inconvenient, but I suspect that you can't then use one,
for example, as an argument to an assumed shape 2D array.
(Maybe you can, I didn't try.). But it gets worse for higher
dimension arrays, with nested loops of C_F_POINTER.

But in both cases, there is the inconvenience of referencing
structure members that COBOL fixed.

First, COBOL only has 1D arrays, but they fix this by allowing
array of structure of array of structure of ...

You might then reference A(I).B(J).C(K).D(L). Just a little
inconvenient. But COBOL allows moving subscripts around,
so you can instead reference A.B.C.D(I,J,K,L). A little better.

Then, as long as it isn't ambiguous, partial qualification allows
you to remove unneeded structure qualifiers. You could
reference A.D(I,J,K,L) or even just A(I,J,K,L) or D(I,J,K,L),
whichever ones are unambiguous.

You can fix this with the C preprocessor, commonly used
with Fortran:

#define A(i,j,k,l) a(i)%b(j)%c(k)%d(l)

(The C preprocessor is case sensitive, even when Fortran isn't.)

So, not hugely inconvenient for 2D, a little worse for 3D and 4D,
but it still takes some extra work to get right.








Robin Vowels

unread,
May 21, 2021, 4:32:36 AM5/21/21
to
On Thursday, May 20, 2021 at 3:14:04 PM UTC+10, gah4 wrote:
> On Wednesday, May 19, 2021 at 6:25:08 PM UTC-7, duhd...@gmail.com wrote:
>
> (snip, I wrote)
> > > Traditionally in C, arrays are declared with constant bounds.
> > > As well as I know, this has been changing in recent versions of the
> > > standard, in possibly strange and inconsistent ways.
> (snip)
>
> Fortran 66 added, and Fortran still allows, variables for dummy arguments.
.
FORTRAN has always allowed variable names as dummy arguments.
Perhaps you mean, variable names as the bounds of a dummy argument.
.
Such provided a facility called "adjustable dimensions".
0 new messages