- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have a Fortran DLL code that includes a lot of modules. The DLL is called by a C# code. I am trying to make the program exit gracefully without crashing if an error is encountered on the Fortran side. Also, I'd like to send error messages back to the C# side.
I am aware from some research that error trapping has been an issue between C# and Fortran. But, is there any update on that? Can the most recent compilers trap errors in Fortran between C# and Fortran?
Link Copied
- « Previous
-
- 1
- 2
- Next »
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I see you put stop at the end of the handler function. Even for this kind of exception, the program cannot return to a caller caller if I put a return statement instead?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Some errors are continuable, some are not. This one is not. (There is an argument to the handler that tells you which it is.) Just what would you want to return to for an uninitialized variable? Read the documentation for ESTABLISHQQ for details on how it is used.
It sounds to me as if you are relying on exception handling as a substitute for defensive programming. Uninitialized variables are simply programming errors, and you should not be looking to "recover" from them.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I used the uninitialized error as an example. I have a simple question. Can the traceback information or the error message printed? Or can it be reported somewhere so I can access it?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Fortran runtime Traceback can be directed to a file with an environment setting.
setenvqq("FOR_DIAGNOSTIC_LOG_FILE=C:\tracebackqq.txt")
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I tried your sample code that you sent inside a dll project. The handler function can capture division by zero and uninitialized variables but it cannot capture the square root of a negative number even though /fpe:0 is set. This happened only in the DLL project but when I use the console project, it gets captured by the handler as it should. Here are the codes I'm using. Is there something special that needs to be done with DLL projects?
The DLL code is
subroutine check(n1)
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: check
!DEC$ ATTRIBUTES ALIAS:'check' :: check
!DEC$ ATTRIBUTES REFERENCE :: n1
use ifestablish
implicit none
real(8) n
real(8),intent(in) :: n1
procedure(establishqq_handler) :: handler
integer(int_ptr_kind()) :: context
logical :: ret
ret = establishqq(handler, context)
n=(n1)**0.5
print *, n
end subroutine check
function handler (error_code, continuable, message_string, context)
implicit none
logical :: handler
integer, intent(in) :: error_code
logical, intent(in) :: continuable
character(*), intent(in) :: message_string
integer(int_ptr_kind()), intent(in) :: context
character(200) diag
print *, "Exiting due to ", message_string
open(1,file='traceback.txt')
write(1,*)message_string
close(1)
read(*,*)
stop
handler = .true.
end function handler
And here is the caller
program call_check
!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'check' :: check
implicit none
real(8)::n1
n1=-8.0
call check(n1)
end program call_check
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your DLL routine source declares the routine as STDCALL and gives the REFERENCE attribute to the argument N, but the caller doesn't match this. For this example, where it never returns to the caller, that's not fatal, but it would create problems in a real application.
As a fix for this, I just removed the STDCALL and the test code worked in that the handler was invoked for a floating invalid.
If you want to do this in a DLL, where you can't depend on the main program being compiled with /fpe:0, add the option /fpe-all:0 to the DLL under Command Line > Additional Options. There is not a separate property for this option.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It worked on my end also. Is it possible to put the Handler function in a module so that it can be used by different modules?
I tried to do that but I get this error
"error #6401: The attributes of this name conflict with those made accessible by a USE statement. [HANDLER]"
When I use the module that has the function in the DLL subroutine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yes, the handler can be in a module. That error happens when you have a local name that is the same as one that comes from a module.
Be aware that when you establish a handler, that "sticks" until you revert it. You may want to call ESTABLISHQQ again at the end of the routine to remove the handler before returning.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm still struggling with how to declare the handler as a procedure in the main routine, use it in the establishqq call and also include the handler function that has the same name "handler" in a separate module. I'd definitely get an error and the names have to be the same.
I have looked in the documentation on how to revert the handler but I couldn't find help.
Can you please elaborate more?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You don't declare the handler separately if it's in a module you use. As long as the handler itself has the correct interface, you can just leave out the "procedure(establishqq_handler)" line. I put that in the example because I wasn't using a module.
To revert, save the "prev_handler" from the first ESTABLISHQQ call as a procedure pointer, and then pass that as the handler to ESTABLISHQQ at the end of the routine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Okay. It's working now. I have a few questions to understand how the handler function and the establishqq work.
In the handler function, why did you set the value of the handler to "True" after "Stop" statement?
When you say reverting the handler because the value "sticks", what does that mean?
I have tried to follow your instruction blindly and this is what I ended up doing. Is this correct?
subroutine check(n1)
!DEC$ ATTRIBUTES DLLEXPORT :: check
!DEC$ ATTRIBUTES ALIAS:'check' :: check
use interop_c ! the module that has the handler function
use ifestablish
implicit none
real(8) n
real(8),intent(in) :: n1
procedure(establishqq_handler),pointer :: old_handler
integer(int_ptr_kind()) :: old_context,context
logical :: ret
ret = establishqq(handler, context,old_handler,old_context)
n=n1/0
print *, n
ret = establishqq(old_handler, old_context)
end subroutine check
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
By "sticks" I mean that calling ESTABLISHQQ establishes a handler that is global to the currently running program - its effect is not local to the routine where you called ESTABLISHQQ. (In this regard it is different from the OpenVMS LIB$ESTABLISH that I based this on, but VMS has procedure-local, language-independent exception handling baked in, where Windows and Linux do not.)
I set the function result as not doing so will get you a compiler warning - it has no other effect.
Yes, it looks as if you did the revert correctly.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have /fpe-all:0 turned on. The attached fortran works fine with a fortran executable caller. However, the error gets handle by C# when I use a C# caller. I get an error message from C# that there's an arithmetic error. The establishqq command doesn't get to be executed. If I remove /fpe-all:0, nothing happens at all using the fortran and the C# caller. This issue only happens with arithmetic errors (division by zero and sqrt(-ve)). A similar fortran code worked fine with process exceptions. Any ideas why is this happening?
The Fortran Code:
subroutine check_3a(n1)
!DEC$ ATTRIBUTES DLLEXPORT,STDCALL :: check_3a
!DEC$ ATTRIBUTES ALIAS:'check_3a' :: check_3a
!DEC$ ATTRIBUTES REFERENCE :: n1
use interop_c ! the module that has the handler function
use ifestablish
implicit none
real(8) n
real(8), allocatable :: v(:)
character(1) text
real(8),intent(in) :: n1
procedure(establishqq_handler),pointer :: old_handler
integer(int_ptr_kind()) :: old_context,context
logical :: ret
character(13) diag
diag='traceback.txt'
call setenvqq(diag)
print *,'establish handler'
ret = establishqq(handler, context,old_handler,old_context)
print *,'start division'
n=1/n1
print *,'division attempted'
print *, n
return
!read(*,*)
ret = establishqq(old_handler, old_context)
end subroutine check_3a
subroutine check_3b(n1)
The handler module is here.
module interop_c
implicit none
PROCEDURE(CSharpWriteToConsoleInterface), POINTER :: CSharpWriteToConsole
ABSTRACT INTERFACE
SUBROUTINE CSharpWriteToConsoleInterface(i)
INTEGER, INTENT(INOUT) :: i
END SUBROUTINE CSharpWriteToConsoleInterface
END INTERFACE
CONTAINS
!DEC$ ATTRIBUTES DLLEXPORT::SetupFunction
!DEC$ ATTRIBUTES ALIAS:'SetupFunction'::SetupFunction
SUBROUTINE SetupFunction(fPtr)
PROCEDURE(CSharpWriteToConsoleInterface) :: fPtr
INTEGER :: i
! Set Pointer
CSharpWriteToConsole => fPtr
END SUBROUTINE SetupFunction
function handler (error_code, continuable, message_string, context)
!use interop_c
implicit none
logical :: handler
integer, intent(in) :: error_code
logical, intent(in) :: continuable
character(*), intent(in) :: message_string
integer(int_ptr_kind()), intent(in) :: context
character(200) diag
integer :: j
j=error_code
print *, "Handler entered"
print *, "Exiting due to ", message_string
open(1,file='traceback.txt')
write(1,*)message_string
close(1)
CALL CSharpWriteToConsole(j)
!read(*,*)
stop
handler = .true.
end function handler
end module interop_c
CALL CSharpWriteToConsole(j) sends the error code to the C# code if the dll is called by a C# caller.
Thanks!
- Tags:
- @
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Sorry, I have no more I can add here. I don't have a full understanding of how exception handling works on Windows (or Linux), and cross-language exceptions are especially tricky. ESTABLISHQQ can handle exceptions that get seen by the Fortran run-time, but it may be in your case the Fortran run-time never gets a chance to see them.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Attempting (complete) exception handling returns from a Fortran DLL will be incomplete and unsatisfactory at best.
In the C#/C++ compiler designs (that support SEH), exceptions are passed up the call stack where each returning stack level can optionally catch the exception, and more (as) importantly, when not catching and exception, perform the necessary dtor's on function scope allocated objects. Think about this....
Fortran subroutines and functions permit local allocatables, and with recent language standards, include an implicit deallocate of allocated local allocatables. Your contrived SEH method will bypass the implicit deallocations, and thus present a memory leak (should there be any "dangling" allocations).
IOW, your implementation can be successful provided that your code performs no allocations. Allocations could potentially be performed indirectly via a self-written private heap which can be re-initialized upon exit and/or exception from the DLL... but then this precludes having persistent allocated objects within the DLL.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I didn't have any allocation taking place in the code I shared. I wonder why I still can't get the Fortran to catch the error!
Is the procedure pointer contributing to that?
Thanks!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I may have missed but could you share your C# code?
Please keep in mind that most objects and properties are kept in (managed) heap in managed C# (.NET). Even most of the numbers are stored as objects in .NET. You have, in principle, three options.
(1) you can wrap your FORTRAN code in managed code;
(2) you unbox/box all data you share with Fortran before transferring to FORTRAN or back.
(3) allocate all data shared with Fortran in an unmanaged section of C# code.
I. Konuk
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Ibrahim_A_ wrote:
.. Can the most recent compilers trap errors in Fortran between C# and Fortran?
If safe and sustainable ways to do exception handling are of interest, take note there is NO way to do this reliably and consistently and which may also be portable and maintainable, none whatsoever. Now, if you look hard enough online, you may find some hacks here and there but I wouldn't use them for myself so won't recommend them to anyone.
Other than the issue with throwing exceptions with references to uninitialized variables - which is a programming error anyway with what's currently a static compiler-based, trying-to-be-type-safe language such as Fortran and you're off better off handling using more than one FOrtran compiler to test your code, using static analyzers, etc. - Fortran errors are best "caught" by the instructions in yuor code itself e.g., IOSTAT with IO such as read ( unit=lun, .., iostat=.. ); other STAT options with intrinsic subprograms; and IEEE facilities for floating-point exceptions.
Your best bet then is to keep it simple, return error codes and error messages to C# "managed" side either via your APIs and/or some callback mechanisms and do the exception handling on the C# side. Or, move all your code to C#.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Did you try to call Fortran from an "unmanaged" section of C# code? See example here:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ibrahim_A_ wrote:
..
I was able to compile the C++/Fortran codes successfully for matrix multiplication and I added try-catch blocks in the C++ code. I'm still unable to figure out how to send the exception messages to C++ from fortran. For example, how can I pass an exception that there's a number being divided by zero or uninitiated variable?
The fortran code can detect them using Run-time error detection, but the fortran code stops working once these issues are detected without going back to the C++ code. ..
Since you're replying to the post by @Ibrahim_K_ where the "do not do anything fancy" is mentioned and which can be interpreted as keep-it-simple, an option you can consider is for the Fortran DLL to include a callback functionality to invoke a C++ function and that may be a simple way to handle this.
Such a C++ callback function can be invoked when an error exception can be "recognized" in Fortran e.g., using STAT=; IOSTAT= arguments in intrinsic procedures and with IEEE module procedures for floating-point.
This won't "catch" of course things such as uninitialized variables which you will have to check yourself.
The C++ callback can try to "raise" an exception. You can then construct try..catch blocks in C++ around the Fortran DLL procedure invocations.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Ibrahim_K wrote:
I am just about to finish a similar project. I will provide some of the lessons I learned.
Firstly, I link my FORTRAN code with native C++ to generate a static library. Then I wrap the static library in a C++/CLI code. Calls between C# and CLI are managed calls. You can pass any info between like you are within C#. I uploaded here in this forum my project architecture sometimes ago.
1) I do not do anything fancy with memory on the FORTRAN side. If I need play with memory, I do it in C++. You can pass std::vector structures to FORTRAN safely.
..
3) I do not do any fancy I/O in FORTRAN.
Since things like division by zero do not produce exceptions (in the sense that FORTRAN generated code continues with "Nan" value), you can check them at a later point or even display it. ..
C++ objects generally and specifically the STL 'class' instances such as those of std::vector are technically not interoperable with Fortran, readers will benefit from your clarification re: "pass std::vector structures" and what you meant by "safely".
You mention a couple of times you "do not do any fancy" stuff in Fortran, what would be "fancy" according to you?
Re: floating-point exceptions, you write, "you can check them later point .." but deferring action on such exceptions is detrimental in most situations involving numerically intense and sensitive applications. So why defer action on this count?

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page
- « Previous
-
- 1
- 2
- Next »