Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
309 Views

Calling C++ (cpp objects) from a Fortran subroutine

Is there a way to access a C++ object from a Fortran subroutine? My subroutine is like this:

subroutine fsub(...)

! If first time calling, call to a cpp function and do initialization (i.e., create the c++ object) .

! Call to a cpp function which will access to the c++ object and do calculations.

end

The subroutine "fsub" will be called by the Fortran "main" program many times. I guess the problem is how to keep the c++ object alive each time after the subroutine "fsub" returns and how to access the c++ object in the next time.

I have been stuck and frustrated on this problem for weeks. Appreciate any assistance.

Thanks,

Jason

0 Kudos
4 Replies
Highlighted
Honored Contributor I
309 Views

Jason L. wrote:

Is there a way to access a C++ object from a Fortran subroutine? My subroutine is like this:

subroutine fsub(...)

! If first time calling, call to a cpp function and do initialization (i.e., create the c++ object) .

! Call to a cpp function which will access to the c++ object and do calculations.

end

The subroutine "fsub" will be called by the Fortran "main" program many times. I guess the problem is how to keep the c++ object alive each time after the subroutine "fsub" returns and how to access the c++ object in the next time.

I have been stuck and frustrated on this problem for weeks. Appreciate any assistance.

Thanks,

Jason

@Jason,

Sorry to read you "have been stuck and frustrated on this problem for weeks".

Please note Fortran does NOT provide direct interoperability with C++ classes and Fortran code can have no direct understanding of object instances created in C++.  Rather Fortran provides interoperability with the C companion processor which you will know is similar to what C++ provides with extern "C" entities.  Other more knowledgeable Fortran and C++ experts may be able to guide you toward better solutions toward your needs, but if I understood your post correctly, then what I suggest you try is a wrapper layer using appropriate extern "C" functions for the operations involving your C++ classes and utilize, as is commonly done in C <-> C++ invocations, an opaque pointer to the C++ object instance in these functions.  Do pay close attention to the lifecycle of the C++ instance, holding a reference to the opaque pointer from your Fortran code and to instantiate the class as needed and to delete it when done.  Shown below is an example illustrating this scheme using a simple-minded C++ class for an integer stack.   Note you should be able to make do without any actual C code.

Hope this helps, good luck with your work.

#include <iostream>
using namespace std;

class IntStack
{
public:
   IntStack(int num) {  top = 0; maxelem = num; s = new int[maxelem]; }
   ~IntStack() { if (s != nullptr) delete[] s; }
   void  push(int t)
   {
      if (top == maxelem) return;
      s[top++] = t;
   }
   int pop()
   {
      if (top == 0) return -1;
      return s[--top];
   }
   void display()
   {
     cout << "IntStack::Display\n";
     if (top == 0) { cout << "(empty)\n";  return; }
     for (int t=0 ; t < top ; t++) cout << s << " ";
     cout << "\n";
   }
   int   empty()  {  return top == 0;  }
private:
   int *s;
   int top;
   int maxelem;
};

typedef void * OpaqueObject;

// Function prototypes
extern "C" {
   OpaqueObject GetObject(int);
   void ExerciseObject1(OpaqueObject, int);
   void ExerciseObject2(OpaqueObject);
   void DeleteObject(OpaqueObject);
}

OpaqueObject GetObject(int s_size) {

   IntStack *s = new IntStack(s_size);

   return (OpaqueObject)s;

}

void ExerciseObject1(OpaqueObject foo, int i) {

   IntStack *s = (IntStack *)foo;

   s->push(i);

   return;

}

void DeleteObject(OpaqueObject foo) {

   IntStack *s = (IntStack *)foo;

   delete(s);

   return;
}

void ExerciseObject2(OpaqueObject foo) {

   IntStack *s = (IntStack *)foo;

   s->display();

   return;
}
module m

   use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_null_ptr, c_associated

   implicit none

   private

   interface

      function GetObject( s_size ) result( optr )bind(C, name="GetObject")

         import :: c_int, c_ptr

         implicit none

         ! Argument list
         integer(c_int), intent(in), value :: s_size
         ! Function result
         type(c_ptr) :: optr

      end function GetObject

      subroutine ExerciseObject1( optr, ival ) bind(C, name="ExerciseObject1")

         import :: c_int, c_ptr

         implicit none

         ! Argument list
         type(c_ptr), intent(in), value :: optr
         integer(c_int), intent(in), value :: ival

      end subroutine ExerciseObject1

      subroutine ExerciseObject2( optr ) bind(C, name="ExerciseObject2")

         import :: c_ptr

         implicit none

         ! Argument list
         type(c_ptr), intent(in), value :: optr

      end subroutine ExerciseObject2

      subroutine DeleteObject( optr ) bind(C, name="DeleteObject")

         import :: c_ptr

         implicit none

         ! Argument list
         type(c_ptr), intent(in), value :: optr

      end subroutine DeleteObject

   end interface

   ! Hold Object
   type(c_ptr), save :: obj = c_null_ptr

   public :: Create
   public :: Employ
   public :: Destroy

contains

   subroutine Create( prob_size )

      ! Argument list
      integer(c_int), intent(in) :: prob_size

      if ( c_associated(obj) ) then
         call DeleteObject( obj )
      end if

      obj = GetObject( s_size=prob_size )

      return

   end subroutine Create

   subroutine Employ( dat )

      ! Argument list
      integer(c_int), intent(in) :: dat

      if ( c_associated(obj) ) then
         call ExerciseObject1( obj, ival=dat )
         call ExerciseObject2( obj )
      end if

      return

   end subroutine Employ

   subroutine Destroy()

      ! Argument list

      if ( c_associated(obj) ) then
         call DeleteObject( obj )
         obj = c_null_ptr
      end if

      return

   end subroutine Destroy

end module m
program p

   use, intrinsic :: iso_fortran_env, only : compiler_version

   use m, only : Create, Employ, Destroy

   print *, "Compiler Version: ", compiler_version()

   ! Create C++ object
   call Create( 10 )

   ! Use the object a few times
   call Employ( 1 )
   call Employ( 2 )
   call Employ( 3 )

   ! Destroy the object before program termination
   call Destroy()

   stop

end program p

Upon execution,

 Compiler Version:
 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(

 R) 64, Version 18.0.0.065 Beta Build 20170320

IntStack::Display
1
IntStack::Display
1 2
IntStack::Display
1 2 3
Press any key to continue . . .

 

0 Kudos
Highlighted
Beginner
309 Views

Thank you very much! It seems to be exactly what I need.

0 Kudos
Highlighted
309 Views

Thanks to @FortranFan  for kindly sharing this code. Can you kindly show usage of namespace for classes?

best,

0 Kudos
Highlighted
309 Views

Hello @FortranFan,

I have tested your code, and it works. So thank you. I'm interested in the use of the s array in fortran. I tried to modify it to get the s pointer of C++ and use it in fortran but I failed. I don't know how to declare s in fortran to obtain a subroutine GetS(  s ) bind(C, name="GetS") who would permit me to get it.

I wish you could advise me on this problem.

Thanks.

Romain

0 Kudos