Hello everyone, first post in the forum (and quite a newbie at Fortran)
I have been working on a fortran project which at some point needs to solve electrical circuits. After some thought, we have decided to try and use NgSpice (Ngspice, the open source Spice circuit simulator - Intro). NgSpice is written in C but can be compiled as a shared library, and used from other applications.
I have been learning how to write an interface to C, but I have (apparently) reached a dead-end and I don’t know whether I might have to change my strategy. I will try to be thorough in my explanation but don’t show more code than is necessary.
The summary is: I have a derived type that calls some of the C shared functions in its procedures. One of these shared functions receives a series of callbacks as arguments. These callbacks contain the information of the simulation (voltages, times, etc), and I can access those values when the callback function is being executed. However, I don’t know how to make that information available to my type.
In more detail: I have a module (ngspice_interface_mod) where the interoperable types and functions are defined. ngSpice_init is a function from the shared library which receives a series of callback functions. There are more shared functions for which bindings have been written. SendStat is one of the callback functions.
module ngspice_interface_mod
use iso_c_binding
implicit none
interface
integer(c_int) function ngSpice_Init(&
cbSendChar, cbSendStat, cbControlledExit, cbSendData, &
cbSendInitData, cbBGThreadRunning, returnPtr) bind(C, name="ngSpice_Init")
import :: c_int, c_ptr, c_funptr
type(c_funptr), intent(in), value :: cbSendChar
type(c_funptr), intent(in), value :: cbSendStat
type(c_funptr), intent(in), value :: cbControlledExit
type(c_funptr), intent(in), value :: cbSendData
type(c_funptr), intent(in), value :: cbSendInitData
type(c_funptr), intent(in), value :: cbBGThreadRunning
type(c_ptr), value, intent(in) :: returnPtr
end function
end interface
contains
integer(c_int) function SendChar(output, id, returnPtr) !bind(C, name="SendChar")
type(c_ptr), value, intent(in) :: output
integer(c_int), intent(in), value :: id
type(c_ptr), value, intent(in) :: returnPtr
character(len=:), pointer :: f_output
character(len=:), allocatable :: string
SendChar = 0
call c_f_pointer(output, f_output)
string = f_output(1:index(f_output, c_null_char)-1)
! string = string(index(string,'stdout'):len(string)) ! remove 'stdout'?
write(*,*) 'SendChar: ', trim(string)
if (index('stderror Error:', string) /= 0) then
SendChar = 1
end if
end function
end module
The callback function SendChar arguments have to be those because the callback function needed by the ngSpice_init function in C is typedef int (SendChar)(char*, int, void*)
There is another module (circuit_mod) that defines a type and some type-bound procedures. These are the wrappers around the C functions. Leaving it to the bare-bone minimum, it’s:
module circuit_mod
use ngspice_interface_mod
implicit none
type, public :: circuit_t
real :: voltage
contains
procedure :: init
end type circuit_t
contains
subroutine init(this)
class(circuit_t) :: this
type(c_funptr) :: cSendChar
type(c_funptr) :: cSendStat
type(c_funptr) :: cControlledExit
type(c_funptr) :: cSendData
type(c_funptr) :: cSendInitData
type(c_funptr) :: cBGThreadRunning
type(c_ptr) :: returnPtr
integer :: res
cSendChar = c_funloc(SendChar)
cSendStat = c_funloc(SendStat)
cControlledExit = c_funloc(ControlledExit)
cSendData = c_funloc(SendData)
cSendInitData = c_funloc(SendInitData)
cBGThreadRunning = c_funloc(BGThreadRunning)
res = ngSpice_Init(cSendChar, cSendStat, cControlledExit, cSendData, cSendInitData, cBGThreadRunning, returnPtr)
end subroutine
where the procedures whose definition is missing (as SendStat) are in the interface module.
Let’s say I want to store a voltage in this%voltage when the SendChar function is called. I could not, because the callback not being bound to the type, it knows nothing about the type.
However, if I try to make SendChar into a type-bound procedure, defining it in circuit_mod (and removing its definition from the ngspice_interface_mod) as:
integer(c_int) function SendChar(this, output, id, returnPtr)
class(circuit_t) :: this
type(c_ptr), value, intent(in), optional :: output
integer(c_int), intent(in), value, optional :: id
type(c_ptr), value, intent(in), optional :: returnPtr
character(len=:), pointer :: f_output
character(len=:), allocatable :: string
SendChar = 0
call c_f_pointer(output, f_output)
string = f_output(1:index(f_output, c_null_char)-1)
write(*,*) 'SendChar: ', trim(string)
if (index('stderror Error:', string) /= 0) then
SendChar = 1
end if
end function
the output variable, (which in C would a char*) is null (or I think it0s null, because the fortran pointer f_output is null after calling c_f_pointer.
Long story short: how can my interface capture (and store) anything happening inside the callback functions?
Please let me know which additional information would be useful to properly state the problem. I tried not to get into unnecesary details, but then I might have providing too little information.
Thanks,
Alberto