Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Simple Link Error (Using a nested routine in a module)

Status
Not open for further replies.

GerritGroot

Technical User
Nov 3, 2006
291
ES
Hi,

Somehow I'm doing something wrong in my module as soon as I nest functions. I'll try to explain it by an example:

Imagine that my main program is main.f90:
Code:
PROGRAM Test
USE Math

IMPLICIT NONE

REAL :: y,x=2.2

y=Fourth(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM Test

Using the module math_mod.f90:
Code:
MODULE Math
IMPLICIT NONE

CONTAINS

FUNCTION Fourth(x)
REAL :: Fourth
REAL, INTENT(IN) :: x
REAL :: Sqrd
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

FUNCTION Sqrd(x)
REAL :: Sqrd
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

END MODULE Math

When I compile the above using gfortran:
gfortran -o ./math_mod.o -c ./math_mod.f90
gfortran -o ./main.o -c main.f90
The objects are being made without errors, however when I try to link them to an executable using:
gfortran -o main.exe ./*.o
I get the error:
undefined referene to sqrd_

If I only use the "Sqrd" function in the module it works all fine, but as soon as I nest them to make the "Fourth" function I get this linking error.

There must be something very basic that I'm doing wrong, but what???

Thanks for your help,

Gerrit
 
1. Your linking problem is because you declared REAL :: Sqrd inside of the function FUNCTION Fourth(x).
How can the linker then know, if it have to use the function Sqrd os a number Sqrd.

2. IMHO, declare the type of function rather in header, so you can see at the first sight what's a type the function.
So I would use instead of
Code:
FUNCTION Fourth(x)
REAL :: Fourth
rather this
Code:
REAL FUNCTION Fourth(x)

Here is corrected source
math.f90
Code:
MODULE Math
IMPLICIT NONE

CONTAINS

REAL FUNCTION Fourth(x)
! REAL :: Fourth - rather declare the type of function in the header
REAL, INTENT(IN) :: x
!REAL :: Sqrd - This makes the problem
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

REAL FUNCTION Sqrd(x)
! REAL :: Sqrd - - rather declare the type of function in the header
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

END MODULE Math
math_test.f90
Code:
PROGRAM MathTest
USE Math

IMPLICIT NONE

REAL :: y,x=2.2

y=Fourth(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM MathTest

Compile it with
Code:
$ gfortran math.f90 math_test.f90 -o math_test -ffree-form

And run it with
Code:
$ math_test
   2.2000000       23.425602
 
Hi Mikrom,

Thanks for your answer, it works now.

Nevertheless, I'm asking myself why I got compiler errors in the past for not declaring the variable type of some functions when calling them.

When I call external functions that are not part of a module I always got complaints from the compiler for not declaring it, for example:

Code:
PROGRAM Test
IMPLICIT NONE
REAL :: y,x=2.2
! Functions:
REAL :: Sqrd

y=Sqrd(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM Test


REAL FUNCTION Sqrd(x)
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

Does not work without

Code:
! Functions:
REAL :: Sqrd

Without the above declaration gfortran gives the error:

y=Sqrd(x)
1
Error: Function ´sqrd´at (1) has no IMPLICIT type

But why? Why should it be there in a program and not in a module??

Thanks,

Gerrit
 
Hi GerriGroot,

You need to declare the functions type in a your program because you use [red]IMPLICIT NONE[/red].
See Section 14.1 MIL-STD-1753 Extensions from Professional Programmer's Guide to Fortran77:
IMPLICIT NONE statement
This statement says that there are no default data types in this program unit, so that all named items (variables, arrays, constants, and external functions) must have their data type specified in an explicit type statement.
...

So if you change your code to
Code:
PROGRAM Test
!IMPLICIT NONE
REAL :: y,x=2.2
! Functions:
!REAL :: Sqrd

y=Sqrd(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM Test


REAL FUNCTION Sqrd(x)
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd
it will work.

But why it is so in the main program and not in the module?
I'm not sure, but I think, this is legacy from Fortran77. Modules were introduced later in Fortran90.

The Fortran90's approach is to use modules. So if you want to code your function SQRD in the same source as the main program, you can do it like this

test02.f90
Code:
module my_functions
  implicit none
contains
  real function sqrd(x)
    real, intent(in) :: x
    sqrd=x**2
  end function sqrd
end module my_functions 

program Test
  use my_functions
  real :: y,x=2.2
  y=sqrd(x)
  write(*,*)x,y
  read(*,*)
end program Test

 
Hi Mikrom,

Yes, you are right, it should be declared because I use IMPLICIT NONE, but I also used IMPLICIT NONE in my module, the fact that I didn't use it within the two functions inside the module doesn't make any difference.
If I'd add it there, AND declare Sqrd as a REAL, I'd still get that linking error, which to me doesn't make sense anymore.

Anyway, the code works :)

Thanks,

Gerrit
 
Oops, I forgot to code implicit none in the main program :)
So here is it corrected:

test02.f90
Code:
module my_functions
  implicit none
contains
  real function sqrd(x)
    real, intent(in) :: x
    sqrd=x**2
  end function sqrd
end module my_functions 

program Test
  use my_functions
  implicit none
  real :: y,x=2.2
  y=sqrd(x)
  write(*,*)x,y
  read(*,*)
end program Test
Compile:
Code:
$ gfortran test02.f90 -o test02 -ffree-form
Run:
Code:
$ test02
   2.2000000       4.8400002
 
>but I also used IMPLICIT NONE in my module, the fact that I didn't use it within the two functions inside the module doesn't make any difference.
If I'd add it there, AND declare Sqrd as a REAL, I'd still get that linking error, which to me doesn't make sense anymore.

I don't understand what you mean, if you want post the code.

If we use IMPLICIT NONE in the main program it has Global Scope. So all global variables need to be declared explicitly.
Look at this working example taken from your code:
Code:
PROGRAM MathTest

IMPLICIT NONE

REAL :: y,x=2.2
REAL :: Fourth

y=Fourth(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM MathTest

REAL FUNCTION Fourth(x)
REAL, INTENT(IN) :: x
REAL :: Sqrd ! - It works with or without this
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

FUNCTION Sqrd(x)
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

The declaration
Code:
REAL :: Fourth
is mandatory because the function Fourth() has a global scope - i.e. you compute with it the global variable
Code:
y=Fourth(x)

But the function Sqrd() is not used in Global Scope, (it will be used inside of the function Fourth() -i.e. in local scope only), therefore we don't need to declare it's type explicitly (with the global variables).

But it seems, that no matter if we declare
Code:
REAL :: Sqrd ! - It works with or without this
inside of the function Fouth() or not, the program works.

However we have ssen, that in the module the behaviour was other.
I think it's because the compiler/linker thought, that Sqrd is a local variable of the function Fourth

And why is this behaviour other in the program and in the module?
I think the only reason is the compatibility with the older Fortran 77 compilers. In Fortran 77 this was primary so and after introducing the modules with Fortran 90 the behaviour in the modules was changed, but due to the backward compatibility the behaviour in the program was left unchanged (to be able to compile legacy programs).


 
Thanks,

Nevertheless, it's very inconsistent behaviour, because coding within the module with IMPLICIT NONE inside each function (I don't have the code right here so I can't check it) I'm sure the following module will give a linking error as well, in spite of the IMPLICIT NONE statement INSIDE each function:

Code:
MODULE Math
IMPLICIT NONE

CONTAINS

FUNCTION Fourth(x)
IMPLICIT NONE
REAL :: Fourth
REAL, INTENT(IN) :: x
REAL :: Sqrd
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

FUNCTION Sqrd(x)
IMPLICIT NONE
REAL :: Sqrd
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

END MODULE Math

Gerrit
 
Hi GerritGroot,

1. It's seems to be an inconsistent behaviour, but IMHO it's practical. Why should you bother declaring the type of the function Sqrd() twice ? I don't see the sense therefore. In the module is declared that it CONTAINS these 2 functions and there is no more need to declare the type of one function inside of other function. If one use IMPLICIT NONE inside of a function there is only need to declare all local variables explicitly.
It's good as is - in other programming languages it's similar: Local variables inside of a function need to be declared, but the types of the functions used inside of a function need not be redeclared.
Code:
MODULE Math
IMPLICIT NONE

CONTAINS

FUNCTION Fourth(x)
IMPLICIT NONE
REAL :: Fourth
REAL, INTENT(IN) :: x
!REAL :: Sqrd ! - Why should you bother by this? 
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

FUNCTION Sqrd(x)
IMPLICIT NONE
REAL :: Sqrd
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

END MODULE Math

2. On the other hand I'm bothered if I need to declare the type of function in the main program between global variables, because I used IMPLICIT NONE as well, when the type of the function is declared below. I find it as a stupid feature of Fortran 77.
Code:
PROGRAM MathTest

IMPLICIT NONE

REAL :: y,x=2.2
REAL :: Fourth ! I'm bothered by this

y=Fourth(x)
WRITE(*,*)x,y
READ(*,*)

END PROGRAM MathTest

REAL FUNCTION Fourth(x)
REAL, INTENT(IN) :: x
REAL :: Sqrd ! - It works with or without this
Fourth=Sqrd(x)*Sqrd(x)
END FUNCTION Fourth

FUNCTION Sqrd(x)
REAL, INTENT(IN) :: x
Sqrd=x**2
END FUNCTION Sqrd

3. Therefore if I would need to use Fortran, I would prefer the Fortran's 90 way: Using modules - all over.
Code:
module Math
  implicit none
contains
  real function Fourth(x)
    implicit none
    real, intent(in) :: x
    Fourth=Sqrd(x)*Sqrd(x)
  end function Fourth

  real function sqrd(x)
    implicit none
    real, intent(in) :: x
    sqrd=x**2
  end function sqrd
end module Math

program MathTest
  use Math
  implicit none
  real :: y,x=2.2
  y=fourth(x)
  write(*,*)x,y
  read(*,*)
end program MathTest

Then I don't need to bother about "inconsistent behaviour of IMPLICIT NONE".
:)
 
Yes, you're right, in fact it's the other way round. It's quite stupid that you have to declare it in the non-module case (but I was used to this :) )
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top