- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If I have:
Program DW
implicit none
character(len=50) :: C
logical (kind=1) :: L
C="4 .GT. 3 .And. 5 .EQ. 2"
L=C ! I know that this is impossible
If (L .EQV. .True.) Then ! <--- How can I do This?
Write(*,*) "This is True"
Else
Write(*,*) "This isn't True"
EndIf
End Program DW
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You want Fortran logical expressions in a string to be evaluated? There is no support for that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I need to read "C" from a file. I do not know to solve this.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are asking for code that will function as a runtime expression evaluator. There are some solutions of this nature, but only for arithmetic/mathematical expressions. See, for example, http://gbenthien.net/strings/ (see the third item on the page).
You ask for a mixed logical/arithmetic evaluator. That is a bit more complicated; you have to parse the string into arithmetic expressions, which are evaluated first, then the results are used with comparison operators to obtain boolean results, and a final evaluation is to be made with boolean operators ( &, |, etc.).
Fortran does not attempt to interpret what is within a character string. Neither "Thou Shalt Not Kill" nor "F = m a" are strings with any specific significance. Therefore, if you wish to infer meanings, you will have to write code to parse and evaluate the string.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A rather crude way is to have your program write a separate Fortran program with your expression (though how you get the variables in there depends on what it is you're trying to accomplish. Then you would have to compile. link and run this program and retrieve the result.
For communicating variables you may find NAMELIST I/O to be of use. But for evaluating expressions, you're on your own.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
There are a few Fortran expression parsers out there that you can google up. In Windows, a more general solution would be to compile a procedure containing the expression you want to a *.dll and then access that procedure via LoadLibrary, GetProcAddress, and C_F_PROCPOINTER. In Linux, I suppose one would compile to a *.so file or a *.dylib file in OSX. See also https://software.intel.com/en-us/node/525275 . Then instead of LoadLibrary and GetProcAddress, in Linux there is dlopen and dlsym http://linux.die.net/man/3/dlopen . Looks like it would be easy to compose a small example, but I don't have a Linux machine (well, except for my TV, but I can't compile anything on that) to test it on, so maybe someone else could give it a try.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I made up an example that works in Windows:
module M
use ISO_C_BINDING
implicit none
private
public GetLastError,LoadLibrary,GetProcAddress,MySub
integer, parameter, public :: &
DWORD = C_LONG, &
HANDLE = C_INTPTR_T
interface
function GetLastError() bind(C,name='GetLastError')
import
implicit none
!DEC$ ATTRIBUTES STDCALL :: GetLastError
integer(DWORD) GetLastError
end function GetLastError
function LoadLibrary(lpFileName) bind(C,name='LoadLibraryA')
import
implicit none
!DEC$ ATTRIBUTES STDCALL :: LoadLibrary
integer(HANDLE) LoadLibrary
character(kind=C_CHAR), intent(in) :: lpFileName(*)
end function LoadLibrary
function GetProcAddress(hModule,lpProcName) bind(C,name='GetProcAddress')
import
implicit none
!DEC$ ATTRIBUTES STDCALL :: GetProcAddress
type(C_FUNPTR) GetProcAddress
integer(HANDLE), value :: hModule
character(kind=C_CHAR), intent(in) :: lpProcName(*)
end function GetProcAddress
subroutine MySub(x)
implicit none
class(*), allocatable, intent(out) :: x
end subroutine MySub
end interface
end module M
program P
use M
use ISO_C_BINDING
implicit none
character(80) expr
real harvest
character(20) libname
integer iunit
integer Estat
integer(HANDLE) Hlib
integer(DWORD) LastError
type(C_FUNPTR) Address
interface
subroutine SetMySub(Address,S)
import
implicit none
type(C_FUNPTR), value :: Address
procedure(MySub), pointer :: S
end subroutine SetMySub
end interface
procedure(MySub), pointer :: S1
class(*), allocatable :: x
expr = '4 .GT. 3 .And. 5 .EQ. 2'
call random_seed()
call random_number(harvest)
write(libname,'(a,i0)') 'MyLib',int(1.0e6*harvest)
open(newunit=iunit,file=trim(libname)//'.f90',status='replace')
write(iunit,'(a)') 'subroutine S(x)'
write(iunit,'(a)') ' implicit none'
write(iunit,'(a)') ' !DEC$ ATTRIBUTES DLLEXPORT :: S'
write(iunit,'(a)') ' class(*), allocatable, intent(out) :: x'
write(iunit,'(a)') ' allocate(x,source=('//trim(expr)//'))'
write(iunit,'(a)') 'end subroutine S'
close(iunit)
call EXECUTE_COMMAND_LINE('ifort /nologo /DLL '//trim(libname)//'.f90',EXITSTAT=Estat)
if(Estat /= 0) then
write(*,'(a,i0)') 'Compilation failed with error status = ',Estat
stop
end if
Hlib = LoadLibrary(trim(libname)//'.dll'//achar(0))
if(Hlib == 0) then
LastError = GetLastError()
write(*,'(a,i0)') 'Loading '//trim(libname)//'.dll failed with error status = ',LastError
stop
end if
Address = GetProcAddress(Hlib,'S'//achar(0))
if(.NOT.C_ASSOCIATED(Address)) then
LastError = GetLastError()
write(*,'(a,i0)') "Accsessing 'S' failed with error status = ",LastError
stop
end if
call SetMySub(Address,S1)
call S1(x)
select type(x)
type is(LOGICAL)
write(*,'(a)') trim(merge("This is True ","This isn't True",x))
class default
write(*,'(a)') 'Error: type not found'
stop
end select
end program P
subroutine SetMySub(Address,S)
use ISO_C_BINDING
implicit none
type(C_FUNPTR), value :: Address
interface
subroutine S2(x) bind(C)
use ISO_C_BINDING
implicit none
type(C_PTR), value :: x
end subroutine S2
end interface
procedure(S2), pointer :: S
call C_F_PROCPOINTER(Address,S)
end subroutine SetMySub
Output:
Creating library MyLib448697.lib and object MyLib448697.exp This isn't True
My best attempt for Linux:
module M
use ISO_C_BINDING
implicit none
private
integer(C_INT), parameter, public :: &
RTLD_LAZY = int(Z'01',C_INT), &
RTLD_NOW = int(Z'02',C_INT), &
RTLD_LOCAL = int(Z'04',C_INT), &
RTLD_GLOBAL = int(Z'08',C_INT), &
RTLD_NOLOAD = int(Z'10',C_INT), &
RTLD_NODELETE = int(Z'80',C_INT), &
RTLD_NEXT = int(-1,C_INT), &
RTLD_DEFAULT = int(-2,C_INT)
public dlopen,dlerror,dlsym,strlen,MySub
interface
function dlopen(filename,flag) bind(C,name='dlopen')
import
implicit none
type(C_PTR) dlopen
character(kind=C_CHAR), intent(in) :: filename(*)
integer(C_INT), value :: flag
end function dlopen
function dlerror() bind(C,name='dlerror')
import
implicit none
type(C_PTR) dlerror
end function dlerror
function dlsym(handle,symbol) bind(C,name='dlsym')
import
implicit none
type(C_FUNPTR) dlsym
type(C_PTR), value :: handle
character(kind=C_CHAR), intent(in) :: symbol
end function dlsym
function strlen(str) bind(C,name='strlen')
import
implicit none
integer(C_SIZE_T) strlen
type(C_PTR), value :: str
end function strlen
subroutine MySub(x)
implicit none
class(*), allocatable, intent(out) :: x
end subroutine MySub
end interface
end module M
program P
use M
use ISO_C_BINDING
implicit none
character(80) expr
real harvest
character(20) libname
integer iunit
integer Estat
type(C_PTR) Hlib
type(C_PTR) LastPtr
integer(C_SIZE_T) LastLen
character(:,C_CHAR), pointer :: LastError
type(C_FUNPTR) Address
interface
subroutine SetMySub(Address,S)
import
implicit none
type(C_FUNPTR), value :: Address
procedure(MySub), pointer :: S
end subroutine SetMySub
end interface
procedure(MySub), pointer :: S1
class(*), allocatable :: x
expr = '4 .GT. 3 .And. 5 .EQ. 2'
call random_seed()
call random_number(harvest)
write(libname,'(a,i0)') 'MyLib',int(1.0e6*harvest)
open(newunit=iunit,file=trim(libname)//'.f90',status='replace')
write(iunit,'(a)') 'subroutine S(x)'
write(iunit,'(a)') ' implicit none'
write(iunit,'(a)') ' !DEC$ ATTRIBUTES DLLEXPORT :: S'
write(iunit,'(a)') ' class(*), allocatable, intent(out) :: x'
write(iunit,'(a)') ' allocate(x,source=('//trim(expr)//'))'
write(iunit,'(a)') 'end subroutine S'
close(iunit)
call EXECUTE_COMMAND_LINE('ifort -nologo -shared -fpic '//trim(libname)//'.f90',EXITSTAT=Estat)
if(Estat /= 0) then
write(*,'(a,i0)') 'Compilation failed with error status = ',Estat
stop
end if
Hlib = dlopen(trim(libname)//'.so'//achar(0),RTLD_LAZY)
if(.NOT.C_ASSOCIATED(Hlib)) then
LastPtr = dlerror()
LastLen = strlen(LastPtr)
BLOCK
character(LastLen,C_CHAR), pointer :: temp
call C_F_POINTER(LastPtr,temp)
LastError => temp
END BLOCK
write(*,'(a)') 'Loading '//trim(libname)//'.so failed with error status.'
write(*,'(a)') LastError
stop
end if
Address = dlsym(Hlib,'S'//achar(0))
if(.NOT.C_ASSOCIATED(Address)) then
LastPtr = dlerror()
LastLen = strlen(LastPtr)
BLOCK
character(LastLen,C_CHAR), pointer :: temp
call C_F_POINTER(LastPtr,temp)
LastError => temp
END BLOCK
write(*,'(a)') "Accsessing 'S' failed with error status."
write(*,'(a)') LastError
stop
end if
call SetMySub(Address,S1)
call S1(x)
select type(x)
type is(LOGICAL)
write(*,'(a)') trim(merge("This is True ","This isn't True",x))
class default
write(*,'(a)') 'Error: type not found'
stop
end select
end program P
subroutine SetMySub(Address,S)
use ISO_C_BINDING
implicit none
type(C_FUNPTR), value :: Address
interface
subroutine S2(x) bind(C)
use ISO_C_BINDING
implicit none
type(C_PTR), value :: x
end subroutine S2
end interface
procedure(S2), pointer :: S
call C_F_PROCPOINTER(Address,S)
end subroutine SetMySub
But you will have to debug it yourself :)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This is a very instructive contribution from Repeat Offender! I modified it to repeatedly read constant expressions from the keyboard and output the results. It takes about 0.4 seconds to read an expression, output the corresponding DLL source, get IFort to compile the code into a DLL, and call the DLL to evaluate the expression.
One expression that I tried,
4.gt.3 .eqv. 1.eq.2
caused the Intel compiler (16.0.1, 32-bit and 64-bit, on Windows) to run into an ICE. I added a main program to the subroutine code generated by Repeat Offender's program to make it stand-alone. The program is fine, but IFort runs into an ICE when compiling it.
program pgm
class(*) , allocatable :: x
call s(x)
select type(x)
type is(LOGICAL)
write(*,'(a)') trim(merge("This is True ","This isn't True",x))
class default
write(*,'(a)') 'Error: type not found'
stop
end select
contains
subroutine S(x)
implicit none
class(*), allocatable, intent(out) :: x
allocate(x,source=(4.gt.3 .eqv. 1.eq.2))
end subroutine S
end program
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well, isn't that exciting? Thanks, I'll write this up for the developers. Issue ID DPD200380554.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Another thing I noticed (this was my first experiment with unlimited polymorphism) was those extra parentheses you can see around the SOURCE= expression are unnecessarily required by ifort. This program fails to compile with ifort, although gfortran passes it:
program P implicit none class(*), allocatable :: als ! ifort and gfortran can't handle this yet ! als = 4 .GT. 3 .And. 5 .EQ. 2 allocate(als,source=4 .GT. 3 .And. 5 .EQ. 2) select type(als) type is(logical) write(*,*) als end select end program P
Also, if someone would try my Linux attempt and post the first error message displayed, I would perhaps be able to produce something that works on that platform. It seems that OSX would be quite similar: just change the command line and also change the .so suffix to .dylib .
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, RO. Escalated as issue DPD200380559.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The ICE RO reported is fixed in the next major release.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page