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

Type Paremeter

amir_445
Beginner
566 Views

Hi there,

I read some numbers, as input to my code, from a text file. I want to set them as Parameter type to make sure their value is not being changed. Would that be possible?

Thanks! 

 

0 Kudos
8 Replies
Arjen_Markus
Honored Contributor I
566 Views

Well, you can not do that as a parameter in the Fortran sense of the word, as that is a compile-time concept.

Fortran also doesn't have a concept of read-only variables, in the sense of setting the value once and then they are immutable, but what you can do is something along these lines:

module readonly_values
    implicit none 
    type readonly
         private
         real :: value
    end type readonly

contains 
subroutine set_value( var, value )
    type(readonly) :: var

    var%value
end subroutine set_value

real function get_value( var )
    type(readonly), intent(in) :: var

    get_value = var%value
end function get_value
end module readonly_values

If you then add suitable overloaded operators and assignments, such variables behave as if they are ordinary variables, with the exception that their value can only be set via the set_value routine.

 

 

 

0 Kudos
andrew_4619
Honored Contributor II
566 Views

Also if you read the data in a main program you could pass the data to a sub-program to do all the work and have the data items as intent(in) arguments. Thus any attempt to modify in the sub-program would be a compile time error.

 

 

0 Kudos
FortranFan
Honored Contributor II
566 Views

You may also want to look into "PROTECTED" attribute that is part of the Fortran standard: see here https://software.intel.com/en-us/node/526549.

Your data - the numbers you read in - can be made variables in a some "data" module and given the PROTECTED attribute; a procedure in this "data'' module can read them in from the file(s).  You can then make other procedures that need these values to be outside the scope of this "data" module i.e., "contained" in other modules.  These other procedures can access the numbers via USE statements on the "data" module.  Owing to the PROTECTED attribute, these calling procedures will not be able to change the numbers and any attempt to do so can be caught by the compiler.

 

 

0 Kudos
JVanB
Valued Contributor II
566 Views

So ifort has PROTECTED now? Let's give it a test drive:

module M
   implicit none
   integer, PROTECTED :: i
   contains
      subroutine SET_I(J)
         integer J
         I = J
      end subroutine SET_I
end module M

program P
   use M
   implicit none
   call SET_I(10)
   write(*,*) I
   call S(I)
   write(*,*) I
end program P

subroutine S(I)
   implicit none
   integer I
   I = 20
end subroutine S

Output is:

          10
          20

So the protection is only syntactic. If you want protection to the extent of crashing the program on an attempt to change your variables, I think the only thing that will work is VirtualAlloc.

 

0 Kudos
Steven_L_Intel1
Employee
566 Views

We have had PROTECTED for quite a while. Indeed it is only syntactic and can be gotten around. In the case you cite, since S has no interface the compiler doesn't know what it is doing with the argument. You can think of PROTECTED as similar to INTENT.

C551 A nonpointer object that has the PROTECTED attribute and is accessed by use association shall not appear in a variable definition context (16.6.7) or as the data-target or proc-target in a pointer-assignment-stmt .

That's really all it is.

0 Kudos
JVanB
Valued Contributor II
566 Views

Couldn't resist an example with VirtualAlloc!

module M
   use ISO_C_BINDING
   implicit none
   private
   public set_string
   public string
   character(LEN=:,KIND=C_CHAR), pointer, protected :: string
   integer, parameter :: SIZE_T = C_SIZE_T
   integer, parameter :: DWORD = C_LONG
   integer, parameter :: BOOL = C_INT
   integer(DWORD), parameter :: &
      MEM_COMMIT = int(Z'00001000',DWORD), &
      MEM_RESERVE = int(Z'00002000',DWORD), &
      MEM_RESET = int(Z'00080000',DWORD), &
      MEM_RESET_UNDO = int(Z'1000000',DWORD), &
      MEM_LARGE_PAGES = int(Z'20000000',DWORD), &
      MEM_PHYSICAL = int(Z'00400000',DWORD), &
      MEM_TOP_DOWN = int(Z'00100000',DWORD), &
      MEM_WRITE_WATCH = int(Z'00200000',DWORD)
   integer(DWORD), parameter :: &
      PAGE_EXECUTE = int(Z'10',DWORD), &
      PAGE_EXECUTE_READ = int(Z'20',DWORD), &
      PAGE_EXECUTE_READWRITE = int(Z'40',DWORD), &
      PAGE_EXECUTE_WRITECOPY = int(Z'80',DWORD), &
      PAGE_NOACCESS = int(Z'01',DWORD), &
      PAGE_READONLY = int(Z'02',DWORD), &
      PAGE_READWRITE = int(Z'04',DWORD), &
      PAGE_WRITECOPY = int(Z'08',DWORD), &
      PAGE_GUARD = int(Z'100',DWORD), &
      PAGE_NOCACHE = int(Z'200',DWORD), &
      PAGE_WRITECOMBINE = int(Z'400',DWORD)
   interface
      function VirtualAlloc(lpAddress,dwSize,flAllocationType, &
         flProtect) bind(C,name='VirtualAlloc')
         import
         implicit none
!DEC$ ATTRIBUTES STDCALL :: VirtualAlloc
!GCC$ ATTRIBUTES STDCALL :: VirtualAlloc
         type(C_PTR) VirtualAlloc
         type(C_PTR), value :: lpAddress
         integer(SIZE_T), value :: dwSize
         integer(DWORD), value :: flAllocationType
         integer(DWORD), value :: flProtect
      end function VirtualAlloc
   end interface
   interface
      function VirtualProtect(lpAddress,dwSize,flNewProtect, &
         lpflOldProtect) bind(C,name='VirtualProtect')
         import
         implicit none
!DEC$ ATTRIBUTES STDCALL :: VirtualProtect
!GCC$ ATTRIBUTES STDCALL :: VirtualProtect
         integer(BOOL) VirtualProtect
         type(C_PTR), value :: lpAddress
         integer(SIZE_T), value :: dwSize
         integer(DWORD), value :: flNewProtect
         integer(DWORD), intent(out) :: lpflOldProtect
      end function VirtualProtect
   end interface
   contains
      subroutine set_string
         character(100) input
         integer(SIZE_T) size
         type(C_PTR) cptr
         integer(BOOL) r4
         integer(DWORD) lpflOldProtect
         write(*,'(a)',advance='no') 'Enter your string:> '
         read(*,'(a)',advance='no',EOR=10,size=size) input
            read(*,'()')
            write(*,'(a)') 'Error in set_string: input too long.'
            stop
      10 continue
         cptr = VirtualAlloc(C_NULL_PTR,size, &
            IOR(MEM_COMMIT,MEM_RESERVE),PAGE_READWRITE)
         if(.NOT.C_ASSOCIATED(cptr)) then
            write(*,'(a)') 'Error in set_string: memory allocation failed.'
            stop
         end if
         BLOCK
            character(LEN=size,KIND=C_CHAR), pointer :: fptr
            call C_F_POINTER(cptr,fptr)
            string => fptr
         end BLOCK
         string = input
         r4 = VirtualProtect(cptr,size,PAGE_READONLY,lpflOldProtect)
         if(r4 == 0) then
            write(*,'(a)') 'Error in set_string: protection failed.'
         end if
      end subroutine set_string
end module M

program P
   use M
   implicit none
   call set_string
   call callback(string)
   contains
      subroutine callback(MyString)
         character(*) MyString ! Read only
         character(*), parameter :: string = 'Not the actual string!'
         write(*,'(a)') 'The input string was: '//MyString
         MyString = 'Garbage' ! Attempt to change MyString
         write(*,'(a)') 'The changed string was: '//MyString
      end subroutine callback
end program P

I/O with ifort:

Enter your string:> This is my string.
The input string was: This is my string.
forrtl: severe (157): Program Exception - access violation
Image              PC                Routine            Line        Source

VA.exe             00007FF630852BF3  Unknown               Unknown  Unknown
VA.exe             00007FF630802902  Unknown               Unknown  Unknown
VA.exe             00007FF630801111  Unknown               Unknown  Unknown
VA.exe             00007FF6308682BE  Unknown               Unknown  Unknown
VA.exe             00007FF6308523EB  Unknown               Unknown  Unknown
KERNEL32.DLL       00007FFCBBD416AD  Unknown               Unknown  Unknown
ntdll.dll          00007FFCBDC6EB64  Unknown               Unknown  Unknown

And as anticipated, we have created a program that would rather die than change its input data. Surprising to me that PROTECTED and PRIVATE are not mutually exclusive. I anticipated three states: PUBLIC, PRIVATE, and PROTECTED, but it looks like Fortran defines 4 state: PUBLIC and PROTECTED, PUBLIC only, PRIVATE and PROTECTED, and PRIVATE only. What good is PRIVATE and PROTECTED?

 

0 Kudos
Steven_L_Intel1
Employee
566 Views

I guess the committee didn't think it worthwhile to add a prohibition on specifying both. As you suggest, if an entity is PRIVATE then PROTECTED adds no value. But it does no harm either.

0 Kudos
jimdempseyatthecove
Honored Contributor III
566 Views

RO, thanks for the VirtualAlloc with read only. Using the PROTECTED would protect from the (virtually all) unintended modifications, the VirtualAlloc and setting page to write protected would catch most of the rest, such as calling an interop C function with reference to the protected variable.

Jim

0 Kudos
Reply