Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29285 Discussions

Sending C++ struc and class Recs to a Fortran Subroutine

Frank_M
Beginner
3,612 Views
How do I send a c++ class to a fortran subroutine?
For example

class foo
{
public:
int m_datalen;
double* m_pfoo_data;

foo(int datalen)
{
m_datalen = datalen;
m_pfoo_data = (double*)_mm_malloc(datalen*sizeof(double),16);

}

~foo()

{
_mm_free(m_pfoo_data);
m_pfoo_data = 0;
m_datalen = 0
}
}

SoI want to be able to define a type foo in fortran that can be assigned to a subroutine dummy variable. I understand there is a way to do this using the a common block.
Thanks.

0 Kudos
1 Solution
jimdempseyatthecove
Honored Contributor III
3,612 Views
The C++ struct had an integer (assuming 32-bits) followed by a pointer to doubles and not an array of doubles.
This is a 8 byte structure on 32-bit platform and 12 byte structure on 64-bit platform.

type foo
sequence
integer(4) :: datalen
integer(C_PTR) :: p_data
end type foo

Then you need to use something link thesubroutine C_F_POINTER

CALL C_F_POINTER(cptr, fptr [,shape])

Jim Dempsey

View solution in original post

0 Kudos
15 Replies
netphilou31
New Contributor III
3,612 Views
Hi Franck,

Have you tried to simply call the Fortran routine by passing the address of the foo object and defining on the Fortran side a derived type containing the public data like:

[fortran]subroutine called_from cpp(dummy_arg)

  implicit none

  type foo
    sequence
    integer(4) datalen
    real(8) m_p_foo(datalen)
  end type

  type (foo) dummy_arg

  ../..

[/fortran]
I am not sure that the sequence statement is mantadory but i guess that yes.

I am doing this frequently not from C++ but from Delphi.
0 Kudos
TimP
Honored Contributor III
3,612 Views
Any reason why the data types and function name shouldn't be set up with iso_c_binding ?
Yes, if you really want to force a double precision array to an odd alignment, you will require sequence.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,613 Views
The C++ struct had an integer (assuming 32-bits) followed by a pointer to doubles and not an array of doubles.
This is a 8 byte structure on 32-bit platform and 12 byte structure on 64-bit platform.

type foo
sequence
integer(4) :: datalen
integer(C_PTR) :: p_data
end type foo

Then you need to use something link thesubroutine C_F_POINTER

CALL C_F_POINTER(cptr, fptr [,shape])

Jim Dempsey

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,612 Views
Just take that SEQUENCE out, please, unless you really want to put it in a COMMON. See here for a can of worms it opens. Additionally, reverse the order of attributes: "biggest first" is a good rule of thumb to avoid alignment problems.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,612 Views
Jugoslav,

The issue you referenced relating to sequence related to pack or not with regard to fields within a record.
SEQUENCE will perform two functions:

1) sequence the fields in the order written in the source code
2) pack the fields together.

When the C/C++ record does not use packing, then you may have a problem as expressed in the other thread.

C/C++ orders the fields as written in source code but may pad fields to natural alignments. So you may have interoperability issues between FORTRAN and C/C++ when the struct layouts are not rigidly controlled on both sides.

Without SEQUENCE, the FORTRAN compiler is free to reorganize the order of the variables such to attain the natural alignments with fewer pads.

The safest way, would likely be a combination of SEQUENCE and UNIONs, and additionaly have a conditional compiled option (for both languages) to produce the offsets to the fields on each for use in an ASSERT.

Jim Dempsey
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,612 Views

Without SEQUENCE, the FORTRAN compiler is free to reorganize the order of the variables such to attain the natural alignments with fewer pads.

That is correct, but no Fortran compiler in the existing universe actually does that. There are compilers where sequence affects padding though.

Quoting jimdempseyatthecove
The safest way, would likely be a combination of SEQUENCE and UNIONs, and additionaly have a conditional compiled option (for both languages) to produce the offsets to the fields on each for use in an ASSERT.

The safest way is actually to use BIND(C) on the structure, which also prevents the reordering, should the Intel folks change their mind one day.

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,612 Views
Further comments:

On the C/C++ side always use a packing of 1. This forces you to be cognisant of natural alignment issues within the struct if you want to address alignment for performance. As Jugoslav suggests, placing the larger member variables first generally is recommended. However, this does not resolve all issues. The totalstruct size is also an issue when you have an array of these structs. The conditional compiled option for ASSERT checking member variable offsets within the struct can also be extended to check for natural alignment. Then the asserts can now inform you of missing (required) pads.

[cpp]// padd.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stddef.h"
#include 

#pragma pack( push )
#pragma pack( 1 )
__declspec (align(64) )
struct foo_t
{
	__int32	n;
	double	d;
};
#pragma pack( pop )


#define TEST_align(s,m) 
	if(offsetof(s,m)%(sizeof((((foo_t*)0)->m))))  
	std::cout << "pad " #s " " #m " " << (offsetof(s,m)%(sizeof((s*)0)->m)) << std::endl;
#if defined(TEST_align)

void aligns_tests()
{
	TEST_align(foo_t, n);
	TEST_align(foo_t, d);
}
#endif

int _tmain(int argc, _TCHAR* argv[])
{
	foo_t	foo;
#if defined(TEST_align)
	aligns_tests();
#endif
	return 0;
}

[/cpp]

On x32 platform you will see:

pad foo_t d 4

Jim Dempsey
0 Kudos
Frank_M
Beginner
3,612 Views
I want to thank everyone for their help.
The most practical solution for the above example turns out to be

[bash]MODULE input_data
    USE, INTRINSIC :: ISO_C_BINDING
    IMPLICIT NONE
    TYPE, BIND(C) :: CData
        INTEGER(C_INT) :: m_datalen
        TYPE(C_PTR) :: m_foo_data
    END TYPE CData
    REAL(8), DIMENSION(:), POINTER :: f_foo_data 
END MODULE input_data

SUBROUTINE FOO_PROCESS(dataRec)
    USE input_data
    IMPLICIT NONE
    TYPE(CData), INTENT(INOUT) :: dataRec
    
    CALL C_F_POINTER(dataRec%m_foo_data, f_foo_data, [dataRec%m_datalen])
       
    RETURN
END SUBROUTINE FOO_PROCESS[/bash]
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,612 Views
Frank,

You haven't shown the C/C++ side of the problem.

The C/C++ struct could be using pack(1) or pack(other), but most likely a pack set by something else preceeding your struct definition.

BIND(C) on the FORTRAN side will not effect your choice of packing on the C/C++ side.

If BIND(C) implies SEQUENCE (with its implicit PACK=1), then the C/C++ side requires explicit packing of 1.

If BIND(C) implies natural alignment with member variables in order specified, then the C/C++ side must also use an alignment attribute such to produce natural aligned membervariables. One cannot assume anything about a default alignment.

Jim Dempsey
0 Kudos
IanH
Honored Contributor III
3,612 Views
BIND(C) implies whatever packing the "companion processor" uses, the processor can be viewed as being the combination of C compiler plus (required/allowed) set of command line switches (or pragmas I guess) that makes the C compiler play ball. Whatever the requirements are, you are one step closer to getting what you want with BIND(C) than you are with SEQUENCE.

Anyway, if you are going to worry about pragma's and command line switches changing alignment from whatever the C processor used by default then you need to worry that same variation on the fortran side too - because that could be changed just as easily.

For my edification: I always thought (possibly erroneously) that byte packing in common (and hence in sequence types) was an implementation detail in the general sense (beyond the requirements for two default reals in a double precision or complex, sizeof logical = integer = real on the second sunday of the month, etc). So if you had a type:

type silly
sequence
character(3) :: a
real :: b
end type silly

then there could be zero bytes, one byte, or four hundred and three bytes of "padding" in between %a and %b, it just needs to be the same everywhere that sequence of types appears in a storage association context. Or am I deluded?

Not that I use common, and hence not that I use sequence...
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,612 Views
type silly
sequence
character(3) :: a
real :: b
end type silly

I believe the above would be called a "character sequence type" and b would start at the 4th byte of the type.
Equivilent to:

#pragma pack(1)
struct
{
char a[3];
float b;
};

Note, the FORTRAN programmer should refrain from using "REAL" as this leaves an ambiguity as to the format. Use REAL(4) or better REAL(C_FLOAT).

use, intrinsic :: ISO_C_BINDING
...
type, bind(C) ::silly
sequence
character(3) :: a
real(C_FLOAT) :: b
end type silly
----------------
#pragma pack(push)
#pragma pack(1)
struct silly
{
char a[3];
float b;
};
#pragma pack(pop)

============= or ===========

use, intrinsic :: ISO_C_BINDING
...
type, bind(C) ::notsilly
character(3) :: a
real(C_FLOAT) :: b
end type notsilly
----------------
#pragma pack(push)
#pragma pack(8)
struct notsilly
{
char a[3];
float b;
};
#pragma pack(pop)

****
MS uses default packing of 8 meaning the alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

The programmer should not assume anything about the packing. Any of the 100's, 1000'sof include files could alter the packing from that observed by a "Hello World" example probram.

Now as to if FORTRAN uses the packing of 8 or not with BIND(C) I cannot say. Steve might be able to answer this.

Jim Dempsey
0 Kudos
Steven_L_Intel1
Employee
3,612 Views
BIND(C) is supposed to pack structures exactly the way the "corresponding" C compiler (MSVC) does by default. If it doesn't, let us know.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,612 Views
BIND(C) is supposed to pack structures exactly the way the "corresponding" C compiler (MSVC) does by default. If it doesn't, let us know.


The "gotcha" I am stressing is if you include any header files, and/or use any command line options affecting packing you are never assured of getting default packing. It is very unusual that any C/C++ application calling FORTRAN support subroutines/functions would NOT contain #include "...".

Therefore, I urge you to explicitly specify the packing for the structs passed to FORTRAN. Your source code is not in control of contents of include files nor of command line. So assume:

If you do not declare packing, expect problems (sooner or later).

Same thing with assumption of size of int, INTEGER, long,REAL, LOGICAL, bool, and what happens with unsigned, etc...

Jim Dempsey

0 Kudos
IanH
Honored Contributor III
3,612 Views
I understand what you are saying, but your gotcha applies to the fortran code that uses SEQUENCE as well. For example, the following little snippet, when compiled and run with 11.1.067 with /warn:all /check:all command line on ia32 indicates byte packing for the sequence type (and an appropriate compile time warning about alignment). Add /align to the command line and you'll get four byte alignment. Compile it with gfortran (without anything special on the command line) and you'll get four byte alignment (which I guess answers my question about what sequence requires). Even putting !DEC$ PACK graffitti all through your code won't necessarily help with the last one (similarly it is also conceivable that a C compiler might exist that doesn't understand #pragma pack and will just ignore it, but far less likely I guess).

BIND(C) at least avoids half of the problem, though I'm not sure which half...

[fortran]PROGRAM but_my_stars_never_align
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_CHAR, C_FLOAT  
  IMPLICIT NONE

  TYPE seq_type
    SEQUENCE
    CHARACTER(9) :: a
    REAL :: b
  END TYPE seq_type  
  TYPE(seq_type) :: sqt
  
  TYPE, BIND(C) :: bindc_type
    CHARACTER(LEN=9,KIND=C_CHAR) :: a
    REAL(C_FLOAT) :: b
  END TYPE bindc_type
  TYPE(bindc_type) :: bct
  
  ! If your computer has more bits than mine, then make this Z16.16.
  CHARACTER(*), PARAMETER :: fmt = "(A,': ',99(Z8.8,:,','))"
  
  !****
  
  PRINT fmt, 'sequence: ', LOC(sqt), LOC(sqt%a), LOC(sqt%b)
  PRINT fmt, 'bind(c):  ', LOC(bct), LOC(bct%a), LOC(bct%b)

END PROGRAM but_my_stars_never_align

[/fortran]
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,612 Views

On the C/C++ side always use a packing of 1. This forces you to be cognisant of natural alignment issues within the struct if you want to address alignment for performance.

I understand where you're coming from Jim, but if I may use a Buddhist metaphor, that approach reminds me of wearing a pebble in your shoe in order to be cognisant that suffering exists in this world. I prefer using the tools designed to do the job, and letting the compiler do its part of the job, not worrying about every gory implementation detail.

In a similar mindset, I don't expect that a randomly included, badly written header/include file affects alignment in my code. Should that happen, I would have a word with the author of that code (or ditch it through the window if it's 3rd party). But we can't -- and should not -- protect ourselves against every possible error or blunder in the tools of our environment (3rd party compilers, libraries, header files, hardware, you name it).

0 Kudos
Reply