Hi folks –
Run into an issue with Fortran class inheritance that has me scratching my head. Best demonstrated with an example program (attached).
test_inherit.f90 (1.2 KB)
I had expected the second call statement in the main program to indicate that eval_bar() is getting called – but no, it’s eval_foo(). Which brings me to my question: is there a way to call an overridden procedure of a parent class, in a way that keeps the other procedures of the parent class overridden? With reference to my example program, I’d like print_foo() to recognize that the dynamic type of the self argument is bar_t, and therefore call eval_bar() rather than eval_foo().
cheers,
Rich
If you don’t override print
,
type, extends(foo_t) :: bar_t
contains
! procedure :: print => print_bar
procedure :: eval => eval_bar
end type bar_t
then it appears to work the way you expect it too:
$ ./test_inherit
str isevaled by foo!
str isevaled by bar!
Indeed. But the need to override print comes if I want extend its behavior – add on some functionality before calling print_foo. The kicker is that the dynamic type of self then seems to get forgotten.
1 Like
I think I see your point. Would the following work for you:
subroutine print_bar(self)
class(bar_t), intent(in) :: self
print *, "Add bar behavior"
call print_foo(self)
end subroutine print_bar
Other OO languages seem to get this kind of behavior correct. I’m surprised (and a little worried) that Fortran can’t. Or maybe this is a bug?
Yes, that would work. But then I have to access the print routine via its specific name rather than its binding name. That seems really gnarly.
Maybe like this:
type :: foo_t
contains
procedure, private, non_overridable :: private_print => print_foo
procedure :: print => print_foo
procedure :: eval => eval_foo
end type foo_t
! ...
subroutine print_bar(self)
class(bar_t), intent(in) :: self
print *, "Add bar behavior"
! call self%foo_t%print() ! --> wrong dynamic type
! call print_foo(self) ! --> needs specific name
call self%private_print()
end subroutine print_bar
I suppose it would be cleaner to implement foo’s print
by calling private_print
, instead of binding the same procedure twice. Then children overriding the procedure, can just add the extra functionality they need.
1 Like
Yes there is one, with caveats.
You want to call the procedure from up at foo_t
, but you want to keep it polymorphic as you want the internal calls to be this way. You can’t do it using the parent accessor, because self%foo_t
is of the non-polymorphic type(foo_t)
anyways, so it will call all functions from that same type.
I would do it this way. Declare for the same routine both the print
generic and a nopass
interface to it. So, you can pass any polymorphic downstream type that extends(foo_t)
as an argument to it, it’s just a little uglier to call it but still a oneliner:
type :: foo_t
contains
procedure, nopass, private :: print_foo
procedure :: print => print_foo
procedure :: eval => eval_foo
end type foo_t
![...]
subroutine print_bar(self)
class(bar_t), intent(in) :: self
call self%print_foo(self) ! Ugly but works!
! you were still callling self%foo_t so it's not a big deal uglier
end subroutine print_bar
Full example here
1 Like
That’s a nice combination of the previous two approaches shown. Which communicates better the intent, that it’s to be used as an internal “service” routine?
That’s a nice approach, but will only work if foo_t and bar_t are defined in the same module. Otherwise, the private attribute on print_foo will have to be removed, right?
2 Likes
Just for the record, C++ works the same way as Fortran does:
#include <string>
#include <iostream>
class foo {
public:
void print() { std::cout << "string is " + eval() << '\n'; }
std::string eval() { return "evaled by foo!"; }
};
class bar : public foo {
public:
void print() {
std::cout << "now in bar\n";
foo::print();
}
std::string eval() { return "evaled by bar!"; }
};
int main(int argc, char const *argv[])
{
foo f;
bar b;
f.print();
b.print();
return 0;
}
$ g++-13 -Wall test_inherit.cpp
$ ./a.out
string is evaled by foo!
now in bar
string is evaled by foo!
Edit: the C++ class works the way @rhtownsend expects it would, if I add the virtual
attribute in the base class:
virtual void print() { std::cout << "string is " + this->eval() << '\n'; }
virtual std::string eval() { return "evaled by foo!"; }
Thanks for the reminder about virtual functions – I think that was what I was remembering, in my belief that other OO languages behave in the manner I had described.
I don’t think Fortran allows this kind of virtual procedure support, but I may be wrong…
There is abstract
and deferred
; I tried to use them but it didn’t change the outcome that when the dynamic type is foo_t
it calls its own method, and not the over-riding child one.