- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As far as I know, 'type(*)' is the standard-conforming way to provide interfaces to C routines with dummy arguments (formal parameter in the C terminology) of type 'void*'. However, the use of 'type(*)' imposes several restrictions, e.g. https://www.ibm.com/support/knowledgecenter/SSAT4T_15.1.4/com.ibm.xlf1514.lelinux.doc/language_ref/assumedtypeobj.html
Since I want to call a C routine with 'void*' argument from Fortran, I have the following questions
- Is there an alternative to the use of 'type(*)'? What about simply calling the C-function without defining an interface?
- Is the following code valid? The GNU Fortran compiler rejects it because context_type contains a type-bound procedure which seems to be a valid concern. The Intel compiler compiles it
program main use context_module implicit none type(context_type) :: context call test(context) end program main module context_module implicit none type :: context_type integer, private :: foo contains procedure, public :: init => context_init end type context_type contains subroutine context_init(self, foo) implicit none class(context_type), intent(in out) :: self integer, intent(in) :: foo self%foo = foo end subroutine context_init subroutine test(context) implicit none type(*) :: context write(6,*) 'hello world' end subroutine end module context_module
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Dear Martin,
here is how I would do that, very generic, with an extern "C" C/C++ function, an interface and a wrapper
type that contains a c_ptr to the extern C function.
extern "C" void your_c_function (double arg) { .... [C code] } type wrapper_t private type(c_ptr) :: obj end wrapper_t interface subroutine your_c_function (obj, arg) bind(C) import type(c_ptr), value :: obj real(c_double), value :: arg end your_c_function end interface type(wrapper_t) :: wrap contains subroutine your_fortran_function (wrap, arg) type(wrapper_t), intent(inout) :: wrap real(default), intent(in) :: arg call your_c_function (wrap%obj, real (arg, c_double)) end subroutine your_fortran_function
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I haven't yet spotted text in the standard that prohibits passing a type with a type-bound procedure to a TYPE(*). I also can't think of a reason for such a restriction, given the severe limits on what you can do with TYPE(*).
On the other hand, I wonder what it is you expect the C routine to do with such a type. It certainly is not interoperable.
TYPE(*) is indeed intended to be the equivalent of void (void* if you don't also say VALUE, assuming BIND(C)).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
.. Since I want to call a C routine with 'void*' argument from Fortran, I have the following questions
- Is there an alternative to the use of 'type(*)'? What about simply calling the C-function without defining an interface?
- Is the following code valid? ..
In response to question 1 and without getting into too many details yet: readers can take note <type(c_ptr), intent(in), value> in Fortran is a reasonable alternative to TYPE(*) option toward the INTERFACE for a C function with <void *> formal parameter.
In response to question 2, I think the code in the original post conforms to what one can expect will be the next revision (Fortran 2018) of the standard to be published in the near future; current "official" standard is technically still Fortran 2008 which doesn't support TYPE(*) i.e., assumed-type that is unlimited polymorphic.
Draft standard for Fortran 2018 constrains the TYPE(*) dummy argument to not have certain attributes (such as INTENT(OUT), ALLOCATABLE, POINTER, VALUE, etc,). Also there are constraints on where the variable with TYPE(*) characteristic can appear. But none of these constraints seem to apply to code in the original post. I think gfortran got it wrong.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
thanks a lot for the quick and extensive feedback.
Now that it is clarified that the actual clarified that the actual argument to a type(*) dummy argument can have type-bound procedures it seems to be the easiest way to go. Note that this contradicts 'An assumed-type dummy argument cannot correspond to an actual argument of a derived type that has type parameters, type-bound procedures, or final subroutines.' from above cited IBM page.
Since I still have an unresolved issue, I'll give some details. The Fortran interface is for PETSc (http://www.mcs.anl.gov/petsc/), an exemplary routine looks like
PetscErrorCode SNESSetConvergenceTest(SNES snes,PetscErrorCode (*SNESConvergenceTestFunction)(SNES,PetscInt,PetscReal,PetscReal,PetscReal,SNESConvergedReason*,void*),void *cctx,PetscErrorCode (*destroy)(void*)) { if (!SNESConvergenceTestFunction) SNESConvergenceTestFunction = SNESConvergedSkip; if (snes->ops->convergeddestroy) { (*snes->ops->convergeddestroy)(snes->cnvP); } snes->ops->converged = SNESConvergenceTestFunction; snes->ops->convergeddestroy = destroy; snes->cnvP = cctx; return(0); }
and the interface routine looks like
Interface subroutine SNESSetConvergenceTest(snes,func,cctx,destroy,ierr) use petscsnesdef type(tSNES) :: snes external :: func type(*) :: cctx external :: destroy type(tPetscErrorCode), intent(out) :: ierr end subroutine end Interface
Still, with or without the interface, I get the following error
/usr/bin/x86_64-linux-gnu-ld: test_snes.o: undefined reference to symbol 'for_set_reentrancy'
/opt/intel/compilers_and_libraries_2018/linux/lib/intel64/libifcoremt.so.5: error adding symbols: DSO missing from command line
PETSc is of course a relatively complex library, therefore I tried to provide the minimal example in the initial post which compiles fine with Intel Fortran. GNU fortran works fine for the PETSc example WITHOUT interface, but complains with the explicit interface
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Diehl, Martin wrote:
.. a type(*) dummy argument .. seems to be the easiest way to go..
Since I still have an unresolved issue, I'll give some details. The Fortran interface is for PETSc (http://www.mcs.anl.gov/petsc/), an exemplary routine looks like
PetscErrorCode SNESSetConvergenceTest(SNES snes,PetscErrorCode (*SNESConvergenceTestFunction)(SNES,PetscInt,PetscReal,PetscReal,PetscReal,SNESConvergedReason*,void*),void *cctx,PetscErrorCode (*destroy)(void*)) { if (!SNESConvergenceTestFunction) SNESConvergenceTestFunction = SNESConvergedSkip; if (snes->ops->convergeddestroy) { (*snes->ops->convergeddestroy)(snes->cnvP); } snes->ops->converged = SNESConvergenceTestFunction; snes->ops->convergeddestroy = destroy; snes->cnvP = cctx; return(0); }and the interface routine looks like ..
In terms of "type(*) dummy argument .. seems to be the easiest way to go", you're arriving at conclusions that were not implied in the responses since the comments were constrained by your original post. TYPE(*) feature introduced in Fortran 2018 has its place but it's not necessarily the "easiest way" under several circumstances.
Anyways, the interface in Fortran you show for PETSc library function is likely the cause of the issues. I have not studied PETSc but assuming the C interface you list is correct, the following would be a suitable interface for it in Fortran:
interface function SNESSetConvergenceTest(snes, pSNESConvergenceTestFunction, pcctx, pdestroy) & result( errorcode ) bind(C, name="SNESSetConvergenceTest") use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_funptr implicit none ! Argument list type(SNES), intent(inout) :: snes type(c_funptr), intent(in), value :: pSNESConvergenceTestFunction type(c_ptr), intent(in), value :: pcctx type(c_funptr), intent(in), value :: pdestroy ! Function result integer(c_int) :: errorcode end function end interface
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ok, I found the relevant text in the standard. In the F2018 draft, 15.5.2.4 (Argument association > Ordinary dummy variables) it says:
"If the actual argument is of a derived type that has type parameters, type-bound procedures, or final subroutines, the dummy argument shall not be assumed type."
Since ifort claims to support this aspect of F2018 (the "further interoperabilty with C" features), it should have caught this. I will file a bug report with Intel.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page