Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Mixed Fortran/C - objects

Kusiek__Adam
Beginner
668 Views

Hello, my problem is as follow: 

I have an exemplary  fortran class: 

      module mod_obj
       
        type object 
            integer :: a, b, c
            real(kind = 8), allocatable, dimension(:,:) :: matrix
        contains
            procedure :: fun2
        end type   
    contains 

        ! Initialize object 
        subroutine fun1(obj, a, b, c)
            type(object), intent(out), allocatable, dimension(:) :: obj
            integer, intent(in) :: a, b, c
            integer :: s
            s = 10
            allocate(obj(1))
            allocate(obj(1) % matrix(s,s))
            obj(1) % a = a
            obj(1) % b = b
            obj(1) % c = c
            ! do some more stuff e.g. calculate values of matrix
        end subroutine 
        
        ! Perform some claculations depending on input argument(s)
        subroutine fun2(this, x)
            class(object) :: this
            integer, intent(in) :: x
            ! do some algebraic operation on matrix depending on x value e.g. matrix = matrix * x
            this % matrix = this % matrix * x
        end subroutine
    
    end module

From C/C++ I would like to:

- first call fun1 to initialize object (and save somehow its handle) 

- call fun2 for different values of x to calculate new values of matrix in my saved object

If it simplify a little on C/C++ I do not need access to fields of Fortran object - actually I need only values of matrix which (in the worst case) I can copy in another Fortran function taking as an argument the pointer to C/C++ matrix. 

Best, 

Adam 

  

 

 

 

  

0 Kudos
11 Replies
Arjen_Markus
Honored Contributor I
668 Views

I have not tried it, but I can imagine that the alternative way to call type-bound procedures will work from the C side:

call fun1( obj, a, b, c )

instead of: call obj%fun1( a, b, c )

You will have to have pointers to the Fortran object and I do not know for sure how the class(...) declaration interferes. In the worst case, you would need a small Fortran wrapper.

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
668 Views

To call fun1, the C code would need to create, initialize and pass a "C descriptor" for an allocatable array of derived type. This is a F18 feature that ifort 16 and later supports. fun1 would also need to have bind(C) added.

fun2 cannot be called from C/C++ due to the CLASS object. You could create another routine, say, fun2C, that accepts it as type(object) and then passes it to fun2, but I would warn you that unless you really need CLASS there (your example does not), you will suffer a severe performance penalty passing a TYPE to a CLASS.

0 Kudos
FortranFan
Honored Contributor II
668 Views

Kusiek, Adam wrote:

.. From C/C++ I would like to:

- first call fun1 to initialize object (and save somehow its handle) 

- call fun2 for different values of x to calculate new values of matrix in my saved object

If it simplify a little on C/C++ I do not need access to fields of Fortran object - actually I need only values of matrix which (in the worst case) I can copy in another Fortran function taking as an argument the pointer to C/C++ matrix. ..

@Kusiek, Adam,

See Quote #2 of this thread where the reverse of what is of interest to you is shown where a C++ class is created, consumed, and freed using procedures in Fortran:

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/734563

You can apply the same concept the other way around with your Fortran "class" i.e., derived type and have suitable interoperable methods to use it from C++. 

 

0 Kudos
Kusiek__Adam
Beginner
668 Views

Hello, thank you for your all responses which are really helpful. In the meantime I have done sth like this: 

module new_type
    public :: fun_dealloc, fun_alloc
    
    type subobj
        integer :: d
        integer :: g
    contains 
        procedure :: setG
    end type 
    
    type obj 
        type(subobj) :: sub
        integer :: a,b,c
        real(kind = 8), allocatable, dimension(:) :: matrix
    end type 
    
    contains

function fun_dealloc(obj_c) result(status) bind(C, name = 'fun_dealloc')

    use iso_c_binding 
    type(c_ptr), value :: obj_c
    type(obj), pointer :: obj_f
    integer :: status 

    call c_f_pointer(obj_c, obj_f)    
    
    if (allocated(obj_f % matrix)) then 
        deallocate(obj_f % matrix)
    end if 
    
    deallocate(obj_f)
    obj_f => NULL()
    obj_c = c_null_ptr 

    status = 1
    
end function
    
    
function fun_alloc() result(obj_c) bind(C, name = 'fun_alloc')

    use iso_c_binding 
    type(c_ptr) :: obj_c
    type(obj), pointer :: obj_f

    allocate(obj_f)
    obj_f % a = 1
    obj_f % b = 2
    obj_f % c = 3
    allocate(obj_f % matrix(20))
    obj_f % matrix(10) = 4
    call setG(obj_f % sub, 5, 6)
    obj_c = c_loc(obj_f)

end function

subroutine setG(this, x, y)
    class(subobj) :: this
    integer :: x, y

    this % d = x
    this % g = y
    
end subroutine

and the C code: 

extern "C"
{
	void* fun_alloc(void);
	int fun_dealloc(void*);
}

int main()
{
	void *p;
	p = fun_alloc();
	int status = fun_dealloc(p);
	return 0;
}

It looks it works but I the code efficiency is also very important to me. 

@Steve - the example I gave before was simple - but it was rather just to show you the idea. In my problem I will need some more complex class with few allocatable matrices and subclasses. So do you think there is other way to solve this ? In fact what I need is just to keep handle to the object allocated in Fortran between successive calls of Fortran subroutines in C/C++. 

Best, 

Adam 

0 Kudos
Steve_Lionel
Honored Contributor III
668 Views

I would use the F18 features for this, as I suggested above. You can even do the allocate and deallocate from C through the CFI_xxx calls declared in ISO_Fortran_binding.h. Construct the descriptor in the C code and pass it to Fortran code as needed.

If I have time this evening I'll turn your code into a worked example. But as I said above, this doesn't work for polymorphic objects and passing a non-poly to a CLASS() dummy will cause a lot of code to be executed on every call.

0 Kudos
Kusiek__Adam
Beginner
668 Views

@Steve 

I will be really grateful for the working code using the proposed by you concept. I will also take a look at this today. I am using Intel Parallel Studio XE 2017 - so is this conecpt available in this version ? 

And the last - what is the reason of overhead in the second example - I only pass pointer to address in memory between Fortran and C and make pointer association using c_f_pointer(). Is this overhead related to c_f_pointer ? 

 

Best, 

Adam 

0 Kudos
jimdempseyatthecove
Honored Contributor III
668 Views

An interoperational issue you have is in Fortran, an allocatable array, or pointer to an array, has an array descriptor as opposed to C (for single dimensioned array) having a pointer to a scalar of the first element of the array, and then elsewhere a variable containing the count of elements. For pre-F18 you might consider declaring your object as:

  module mod_obj
    use iso_c_binding
    type object 
        integer(c_int) :: a, b, c, matrix_s
        integer(c_ptr) :: matrix_c
        real(kind = 8), allocatable, dimension(:,:) :: matrix
    contains
        procedure :: fun2
    end type   
contains 

    ! Initialize object 
    subroutine fun1(obj, a, b, c)
        type(object), intent(out), allocatable, dimension(:) :: obj
        integer, intent(in) :: a, b, c
        integer :: s
        s = 10
        allocate(obj(1))
        allocate(obj(1) % matrix(s,s))
        obj(1) % matrix_s = s
        obj(1) % matrix_c = c_loc(matrix(1,1))
        obj(1) % a = a
        obj(1) % b = b
        obj(1) % c = c

        ! do some more stuff e.g. calculate values of matrix
    end subroutine 
    
    ! Perform some claculations depending on input argument(s)
    subroutine fun2(this, x)
        class(object) :: this
        integer, intent(in) :: x
        ! do some algebraic operation on matrix depending on x value e.g. matrix = matrix * x
        this % matrix = this % matrix * x
    end subroutine

end module

===========
// C/C++
struct object
{
  int a, b, c, matrix_s;
  double* matrix_c;
  // The Fortran array descriptor is hidden
  // objects are created and destroyed on the Fortran side
};

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
668 Views

The overhead comes in when you pass a type(something) to a class(something). When a polymorphic object is received as an argument, it requires that the compiler pass a large data structure describing the class and its parent types, if any. A non-polymorphic object doesn't have this already made, so the compiler creates it on the stack for each call, and this is inefficient. Other implementations are possible, but that's the way it is now in ifort. If you're not calling it a lot, it may not matter, but there have been users here who were astounded how much faster their program ran when they replaced class by type in the dummy argument because they didn't need it to be polymorphic.

I'll work on your example.

0 Kudos
Steve_Lionel
Honored Contributor III
668 Views

I started working on this with the F18 feature, and realized that your type subobj has a type-bound procedure. That's not interoperable. The approach your current code takes, where you use a C_PTR, is probably best for now.

0 Kudos
Kusiek__Adam
Beginner
668 Views

Thank you Steve for your effort. Since I still have some gaps in my knowledge and want to choose the most elegant and efficient way of implementation (and also close this subject) - so lets just summarize what was stated before: 

1. From these two the faster is the second one (when I do not use polymorphism feature)

function fun_mat_slow(obj_f) result(status) 
    class(obj) :: obj_f
    integer :: status, siz
    obj_f % matrix2(5) = 7
    status = 1
end function 

function fun_mat_fast(obj_f) result(status) 
    type(obj) :: obj_f
    integer :: status, siz
    obj_f % matrix2(5) = 7
    status = 1
end function 

2. When I would like to use C descriptor I can use it for these types with removed bounded procedures:

type subobj
     integer :: d
     integer :: g
end type 
    
type obj 
    type(subobj) :: sub
    integer :: a,b,c
    real(kind = 8), allocatable, dimension(:) :: matrix, matrix2
end type 

What is then the benefit in efficiency of using C descriptor comparing to C_PTR?. As I have seen C-descriptor gives access to e.g. matrix both on Fortran and C. Does it also mean that when I have allocatable matrix I can initialize descriptor in C, allocate matrix on Fortran side and deallocate it on C side ? 

3. Does the approach with C_PTR will have higher overhead than the approach with C descriptor ? (assuming I only want to store pointer to object created in Fortran and do not need to have access to its fields in C (the methods also do not have to be bounded in my case) - I do not use polymorphism)

4. Does C descriptor is available in Intel Fortran Studio XE 17 ? 

Best, Adam 

 

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
668 Views

(1) The first case would be faster. The only reason to use class() for a dummy argument is if the procedure is going to do different things depending on the dynamic type.

(2) C descriptors are mainly for when the C code wants to do something with the Fortran object in a way not possible in Fortran 2008. Some examples are:

  • Passing or receiving deferred-shape, allocatable or pointer arrays 
  • Accessing array bounds in C
  • Passing a character value with length greater than 1 or character(*).
  • Wanting to do allocation, deallocation or reshaping of a Fortran array in C code
  • Using DIMENSION(..) assumed-rank

The F18 interoperability features were designed in part to help MPI with interfaces that could use the Fortran shape of the array, and in particular, assumed-rank.

(3) If you are just storing a pointer to the object, then by all means use C_PTR. But last night I realized there is a hitch - ifort may not let you DEALLOCATE a pointer that you reconstructed using C_F_POINTER because that loses the flag ifort uses to specify whether a pointer is allowed to be deallocated, even though, by the words of the standard, the deallocate is allowed. This is a flaw in the design and not easily worked around. What you may end up doing is doing the allocate/deallocate as malloc/free (in Fortran - ifort treats these as intrinsics), casting to a C_PTR with TRANSFER and then using C_F_POINTER. Ugly, I know.

(4) Yes. It was added in version 16.

0 Kudos
Reply