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

adding 64bit configuraiton; updating an old fortran project

Groos__Geoff
Beginner
1,231 Views

I am looking to port and update a visual fortran project written by a scientific genius (read: not one who deals with visual studio idiosyncrasies well) from release-only x86-only to release-or-debug on x86-or-x64. The critical blocking feature is x64 support. nice-to-have's include tests that link the same way our application does (read: via dll) and use of "attach to process: myapp-wrapping-java-calling-mydll.exe" working correctly.

To do this I've created a new solution file and attempted to basically rewrite the vfproj files by hand after they were generated by visual studio. This has been informative and overwhelming.

edit: some notes, "application code" is mostly java and is mostly UI/etc, as distinct from my fortran "science code", which is ifort 2017 compiled to a dll loaded by the java application code.

So far:

  1. I got it building, but I have to call ifort twice every time I want to rebuild, else it screws up the dependency ordering and attempts to compile sub-modules in the wrong order. The original author said "yeah, sometimes ifort does that". I've sort've punted this one because it doesn't seem to block me. My build agents wont like this. TODO.
  2. I re-compiled x86 for debug and release, dropped it into my app. Preliminary tests show everything fine (except now ifort is encoding boolean true as 0XFF instead of 0x01. Probably in the release notes somewhere? Not blocking.)
  3. re-compile for debug|x64, move small mountains in app code to get it on x64, run my app: I get a SIG_SEGV (trapped by JVM and exposed as a InvalidMemoryAccessError) half-way through my native code's execution. So I need to debug!
  4. Run my application, attempt to "attach to process" from visual studio: no luck. Symbols not loaded.
  5. I want to run the science code's embedded tests; I don't know if my science code works at all (read: without the mangling & lazy loading by app-code), so I go to run the handy test functions that are embedded in our release code (?). Unfortunately the vfproj is ConfigurationType="typeDynamicLibrary", and Visual studio doesn't let me change that, so I cant run it directly. I removed the ConfigurationType, got a exe instead of a dll, which, when run, errors out with "Not a valid win32 application". I then tried to create a new vfproj for the tests that would consume its dependency vfproj science code. I'm stumped at how I can/should app such a dependency, since visual studios usual "references" block is missing, and adding the .lib (dll wrapper) in the additional-include directories fails with unknown symbol.

So now I'm stumped. I cant test it, I cant debug it. It works a little bit.

My immediate questions:

1. Whats the easiest way I can add a reference to an existing project (ideally via a static reference)?
2. What do I need to do to get visual studio's "debugger: attach to process" working?

Tomorrow I'll minimize the project to remove source code in an attempt to upload it here. Any help is appreciated.

0 Kudos
21 Replies
Groos__Geoff
Beginner
1,156 Views

Regarding my first problem, with module-dependency building, heres an abridged log from ms build

These are logs from building science-code project, then after it fails simply building it again without clean. The key here is that SOGOIn.f90 is
SUBMODULE (SOGOMain : SOGOC) SOGOIn
and sogoc.f90 is
SUBMODULE (SOGOMain) SOGOC
meaning sogoIn depends on sogoc, yet it tries to build them in the wrong order. Is this a bug?

 

1>------ Build started: Project: LGO, Configuration: Debug Win32 ------
1>Compiling with Intel(R) Visual Fortran Compiler 17.0.8.275 [IA-32]...
1>RealVec.f90
1>JaggedMat.f90
1>blas.f90
1>lapack.f90
1>TestRealVecMat.f90
1>Util.f90
1>TestUtil.f90
1>OptUtil.f90
1>SOGOUtil.f90
1>SOGOCUtil.f90
1>SOGOMain.f90
1>SOGOIn.f90
1>C:\Users\geoff\Code\LGO\src\SOGOIn.f90(4): error #8764: Error in opening the compiled parent submodule file.   [SOGOMAIN@SOGOC]
1>C:\Users\geoff\Code\LGO\src\SOGOIn.f90(7): error #6457: This derived type name has not been declared.   [CONSTMODEL]

[snip, other deps fail too]

1>sogoc.f90
1>SOGOTestSuiteEmbedded.f90
1>
1>Build log written to  "file://C:\Users\geoff\Code\LGO\build\temp\Win32\Debug\BuildLog.htm"
1>LGO - 63 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========





1>------ Build started: Project: LGO, Configuration: Debug Win32 ------
1>Compiling with Intel(R) Visual Fortran Compiler 17.0.8.275 [IA-32]...
1>SOGOIn.f90
1>SOGOTestSuiteEmbedded.f90
1>Linking...
1>Creating library C:\Users\geoff\Code\LGO\build\Win32\Debug\LGO.lib and object C:\Users\geoff\Code\LGO\build\Win32\Debug\LGO.exp
1>nlopt_mingw-x86-debug.lib(DIRect.obj) : warning LNK4099: PDB 'nlopt.pdb' was not found with 'nlopt_mingw-x86-debug.lib(DIRect.obj)' or at 'C:\Users\geoff\Code\LGO\build\Win32\Debug\nlopt.pdb'; linking object as if no debug info
[snip; oops]
1>nlopt_mingw-x86-debug.lib(ags.obj) : warning LNK4099: PDB 'nlopt.pdb' was not found with 'nlopt_mingw-x86-debug.lib(ags.obj)' or at 'C:\Users\geoff\Code\LGO\build\Win32\Debug\nlopt.pdb'; linking object as if no debug info
1>Embedding manifest...
1>
1>Build log written to  "file://C:\Users\geoff\Code\LGO\build\temp\Win32\Debug\BuildLog.htm"
1>LGO - 0 error(s), 45 warning(s)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

 

on the second build, it succeeds on SOGOIn.f90 because on the previous build it suceeded in sogoc.f90.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,156 Views

In MS VS: Right-click on the Solution, Click on Project Dependencies, then using the Project pull-down, walk through each project and assure the dependencies are checked correctly for your build.

Jim Dempsey

0 Kudos
IanH
Honored Contributor II
1,156 Views

Why did you have to write the vfproj's from scratch?

Can you try with a more recent version of the compiler and more importantly, Visual Studio integration?

Given the dependency issue involves a submodule dependency, I don't understand how the build machinery could still be confused, but it is worth checking that there isn't some sort of cyclic dependency between files that arises from the way that modules are defined and used.

How would you normally run the "handy test functions" within your code?

Projects that build DLLs can be debugged, but in the properties for the project you need to specify an executable (in the properties for the project under Debugging > Command) that the debugger can execute.  Sometimes you can use the "normal" executable for this (whatever process the java runtime spawns - I'm not sure how java works in this regard), in other cases it can be useful to have a small wrapper executable that calls your DLL's test procedures appropriately.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,156 Views

Handy tip that works for C# and may work for JVM.

If you can use the MS VS IDE to debug your JavaScript app...

1) add a dummy DLL subroutine

subroutine DebugHelper()
  integer, save :: hack = 0
  hack = 1
end subroutine DebugHelper

2) Early on in your JS _before_ you call your normal DLL function/subroutine, place a call to the DLL DebugHelper
3) start debugging the JS app (not the DLL).
4) place break point on the JS app's call the DebugHelper
5) run to that break point
6) Open a disassembly window
7) With the Disassembly window in focus (IOW do not set the JS script in focus), Debug | Step Into (F11) repeat until you enter the DLL
8) Once in the DLL, you should have the DLL's debug symbols available and you can set break points.

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
1,156 Views

That seems overly complicated to me. What I have done (not with JS but with other languages where the DLL: is dynamically loaded) is to use the DLL project and set the "Command" to be whatever invokes the executable. I then, in VS, open the source file of the DLL routine and set a breakpoint there. Maybe that won't work with JS, not sure.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,156 Views

Steve,

Interesting. Setting the DLL project as the startup project (which is not run-able as such) and command to that of the JS/C# app. I did not try that.

What worked for me in the C# project. In particular when you (I) attempt(ed) to set a break point in the source of the DLL prior to having the DLL loaded. This was back in IVF V17 and V18.

In my case, I had to manipulate break points in both the C# (prior to calling DLL) and in the DLL. I think the set break point problem in your suggestion may revert to the non-startup project (JS/C# though I haven't experimented).

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
1,156 Views

I've done the DLL-as-startup-project with Excel.

0 Kudos
Groos__Geoff
Beginner
1,156 Views

Thanks for the responses. It makes me feel a lot better about this work with you guys here.

In MS VS: Right-click on the Solution, Click on Project Dependencies, then using the Project pull-down, walk through each project and assure the dependencies are checked correctly for your build.

This is exactly what I did; create a second project, added it as a build-dependency, and then updated the test project to the -I "INCLUDE" aka "additional dependencies", where I added the output dir for the "production" project. Seems to work well. Not sure about how I'll handle static-depenedency vs dynamic-dependency though.

Why did you have to write the vfproj's from scratch?
Can you try with a more recent version of the compiler and more importantly, Visual Studio integration?

I am using the visual fortran integration. The existing projects refused to build for x64 or for Debug configurations. It would only build x86 and only build Release. I toggled switches, flipped options, and got tired. So I made a new project from visual studio "new fortran project", added all the existing source code, and modified the project files from there. At this point it builds all configurations for all platforms. It also seems to run all the tests for each of those.

Given the dependency issue involves a submodule dependency, I don't understand how the build machinery could still be confused, but it is worth checking that there isn't some sort of cyclic dependency between files that arises from the way that modules are defined and used.

By cut and pasing the contents of SogoIn.f99 into sogoc.f99, I can now compile without issue. If i had a true cyclic dependency, wouldn't it consistently fail to compile? It seems to me that the fortran compiler simply must compile sogoc.f99 before sogoin.f99. sogoin.f99 had a "use sogoc" directive very near the top, but it somehow isnt getting picked up by the fortran compiler. Maybe this is fixed in a subsequent release? Anyways, for now my workaround is sufficient.

How would you normally run the "handy test functions" within your code?

This is a very good quesiton. I'll ask the original author. I presume he had other projects that he would use as "drivers" that were configured to build an exe, statically linked to the production project, that had one line of "call the test entry point". Though I couldnt find those projects. I suspect he was enabling/disabling code with comments to achieve something to this effect.

Anyways, I've extracted some of the test code into a separate project, and statically linked it, and they run. So I'm happy here.

My next step is to create another driver application that links to the production fortran project dynamically (read: via DLL, the same way our production java code will). My plan here is to have the debug builds be static libs, and the release builds be dll's. Is this at all conventional? Is there a way I can ask for a visual foirtran project to output both static libs and dynamic libs?

Projects that build DLLs can be debugged, but in the properties for the project you need to specify an executable (in the properties for the project under Debugging > Command) that the debugger can execute.  Sometimes you can use the "normal" executable for this (whatever process the java runtime spawns - I'm not sure how java works in this regard), in other cases it can be useful to have a small wrapper executable that calls your DLL's test procedures appropriately.

Handy tip that works for C# and may work for JVM.

That seems overly complicated to me. What I have done (not with JS but with other languages where the DLL: is dynamically loaded) is to use the DLL project and set the "Command" to be whatever invokes the executable.

I'll look into this. But what I'm hoping to learn about is some of the machinery that windows uses to attach a debugger. Do I need a special invocation of windows "Start Process" or a special environment parameter or some such to ask windows to attach debugging facilities to a native DLL? Needless to say, I dont have any control over java.exe, so I need the debugging facilities to be able to get in on very lazily made "LoadDLL" calls made by a process. Is this achieveable?

I know the JVM (and I believe the dotnet runtime) both have directives that add a "debugger listener" on a websocket that your IDE connects to. the source/byte-code dictionary is provided as annotations embedded in the byte code, so there are no separate pdb (or similar) files.

but clearly a TODO is to try setting my dll as the startup app, and then setting the command to the appropriate java invocation. I'll give this a try.

Thanks again for your help,

- Geoff Groos

0 Kudos
IanH
Honored Contributor II
1,156 Views

It seems to me that the fortran compiler simply must compile sogoc.f99 before sogoin.f99. sogoin.f99 had a "use sogoc" directive very near the top, but it somehow isnt getting picked up by the fortran compiler.

What do you specifically mean here?  Do you have a submodule referencing its parent module via USE statement? If so, I recall discussion (F08/0128) about the legality of this from a language standard point of view.  I don't recall and can't see where the discussion got to (such a reference may violate the requirement that a module not reference itself), but that would be the sort of thing that could confuse dependency analysis.

You don't debug a DLL, you debug a process.  That process is (usually, unless you are playing kernel level games) started from an executable.  As part of loading that executable, or as part of the execution stream, DLL's will be loaded and so forth.  There's typically nothing special you need to do when starting the process that you later intend to attached a debugger to (or start with the debugger already attached).  Things can get complicated when the process that you are trying to debug itself spawns child processes as a debugger - so now you have two debuggers in the chain, or identifies that it is being debugged and starts doing things differently.  I've never debugged stuff running through java.exe, so not sure what it does, but in the usual case, what you want to do is straight-forward.

From a simplicity point of view, having a simple driver program that is written in Fortran and can directly call the procedures under test is ideal.

Your observations around a change in the nature of the representation of the LOGICAL data type suggests you haven't transferred across all the project properties when you rebuilt the projects (or defaults for new projects have changed).  In what way did the existing projects "refuse to build"?

0 Kudos
Groos__Geoff
Beginner
1,156 Views

Hi guys, so i've made a lot of progress and am still working through issues one by one. I am currently employing a hack as a way to solve the problem I brought up here.

Do you have a submodule referencing its parent module via USE statement?

I'm sorry I miss-spoke; i meant sogoin is a submodule of sogoc which is a submodule of sogomain. Yet it consistently attempts to compile sogoin before sogoc.

MODULE SOGOMain
SUBMODULE (SOGOMain) SOGOC
SUBMODULE (SOGOMain : SOGOC) SOGOIn

use statements appear only related to some utility classes that themselves are modules that make no use of submodules.

This became something of an idiom on this codebase and is used by a number of other features, all of which fail intermittently in the same way: submodules sometimes fail to compile in the correct order.

My hack to solve this: "exclude from build" SOGOIn.f90, then at the bottom of SOGOC.f90 i have "INCLUDE 'SOGOIn.f90'".

There's typically nothing special you need to do when starting the process that you later intend to attached a debugger to (or start with the debugger already attached).... Things can get complicated when the process that you are trying to debug itself spawns child processes as a debugger

Thanks for the clarification. Neither dotnet nor jvm use child-processes as debuggers. Both listen on websockets to accept commands and intercept loaded code at the GAC/classloader (in a single process space) to break at a breakpoint. I just dont know anything about how native code toolchains achieve the same functionality, but I rather suspect it isnt through websockets.

Given the answers I have here (read: nothing special you need to do when starting... use the command to start java), I'm fairly confident I can get myself into a mode where I can do some debugging on a java invocation. Unfortunately I have yet to try it; I'm still working on my build system.

I've been fighting with getting everything building statically. I have had some success for some configurations, but not all. At this particular moment, it seems both libmmt and libucrt are trying to implement __Clcos.

0 Kudos
Groos__Geoff
Beginner
1,156 Views

ooh boy. I was hoping it was something as simple as debug vs release dlls or x86 vs x64 problems.

as per https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/749133, Steve's own advice is "use dll's".

I really hope i can do better...

0 Kudos
Groos__Geoff
Beginner
1,156 Views

Resolve the above issue with a /FORCE to the linker.

Now I'm into size_t problems. We have a couple of QSORT calls that were all using the builtin INT4(value) for two arguments that need to be INT8(value) calls on x64. To make this source code platform agnostic, they both need to be INTEGER(POINTER_LEN) variables.

My problem now is that INTEGER(POINTER_LEN) doesnt appear to be in scope, despite a use ISO_C_BINDING at the front of the file.

the urge to write my own precompiler maco is real...

0 Kudos
Arjen_Markus
Honored Contributor I
1,156 Views

The right kind constant in ISO_C_BINDING for that is called C_INTPTR_T.

0 Kudos
Groos__Geoff
Beginner
1,156 Views

Arjen Markus wrote:

The right kind constant in ISO_C_BINDING for that is called C_INTPTR_T.

Thanks for this. I noticed that it ISO_C_BINDING was using the fortran builtin INT_PTR_KIND(), so and using that allows me to avoid an import USE statement.

I got it running!

I've also enabled all runtime checks, and it crashes very quickly. It turns out that we had at least one use of an uninitialized variable (an integer that was incremented without being initialized, so thats probably ~safe --I removed it of course) and we had calls to QSORT with an INTEGER(8) on a x86 platform.

I'm still really confused about what happens regarding QSORT calls that expect a size_t. It seems like fortran wont do widening/narrowing for you, so if you call QSORT(arr, INT4(size), ...) on a 64bit system, since I believe fortran is pass-by-reference, you're going to invoke QSORT with a pointer to an on-stack 32bit integer, that qsort will simply interpret as a 64bit integer, thus loading whatever next 4 bytes happen to be on stack? This isn't likely to SEGV because, as per the code above (and infact all idiomatic calls to QSORT), the size_t variables are on stack.

I donno. I'm the CTO at our company and one of the reasons I encouraged our scientists to use fortran instead of C++ was to avoid pointer arithmatic. Yet here we are. I suppose expecting a really high quality platform-agnostic behaviour out of a langauge with as much legacy as fortran was too much.

0 Kudos
Steve_Lionel
Honored Contributor III
1,156 Views

QSORT is a generic with specifics for each intrinsic type/kind combination. It does not support mixed kinds.

0 Kudos
Groos__Geoff
Beginner
1,156 Views

@Steve that was my thinking. As a result, if you call QSORT(..., int4(0)) on an x64 platform (where you must call QSORT(... int8()), shouldn't the use of the wrong integer kind result in a linker failure akin to "cant find a QSORT that takes an Int4" or have the compiler widen/narrow the int to the appropriate type? I'm disappointed that it seems like its giving me undefined behaviour.

I'm also a little puzzled by the fortran communities lack of concern for a SIZE_T. Is this because multi-dimensional arrays alleviate that problem so well, its just assumed that for data structures in access of 4GB, you'll be able to fit them into a multi-dimensional array (eg matrix)? Its an interesting philososphy, and honestly I've always viewed the C++ need for an "index type" as a kind of clooge.

Anyways, I have worked that specific problem; all invocations are updated to use an INTEGER(INT_PTR_KIND()). I'd like to provide a wrapper function for this, but I'm not sure on the parameter type for the `compar` comparator parameter. Does anybody have the QSORT fortran signature code?

I'm at the point now where my production code can load the DLL, however it runs into a number of failures from the /checks:all. I've been working through those, but annoyingly, the stack-trace is always omitted with "Stack trace terminated abnormally."

So I changed my code such that the only thing the function does is "`CALL BACKTRACEQQ()`". This produces:

forrtl: severe (41): insufficient virtual memory
Stack trace terminated abnormally.

Now I'm wondering what could be happening with respect to heap and stack pointers. does "insufficient virtual memory" mean malloc returned null? I want to get the stack-trace facility in fortran working from java, i think this is an important step. Any idea how I might go about debugging this?

0 Kudos
Groos__Geoff
Beginner
1,156 Views

I got it running!

Regarding the above error, TRACEBACKQQ require IFCORE. If you dont USE IFCORE first, it explodes at runtime. Again I would've thought this would be an "unknown symbol" either in linker or compile time. I still have a lot to learn about fortran.

Regarding the last set of errors: /Check all was flagging a pointer provided by a C_F_POINTER as "use before initialization".  I'd really like to keep /check:all on going forward. Any ideas how i might best suppress this? Can I simply initialize these pointers to some constant (eg null) to get rid of this error?

Further, every message from fortran is failing to generate a stack trace, eg:

forrtl: severe (104): incorrect ACCESS= specifier value for connected file, unit 20, file C:\Users\geoff\AppData\Local\Temp\cgo.log
Stack trace terminated abnormally.

This problem with the stack-trace only occurs from (java 13) x64. When called from (java 8) x86, it successfully builds the trace. I have yet to try java 8 x64.

0 Kudos
Steve_Lionel
Honored Contributor III
1,156 Views

Groos, Geoff wrote:

@Steve that was my thinking. As a result, if you call QSORT(..., int4(0)) on an x64 platform (where you must call QSORT(... int8()), shouldn't the use of the wrong integer kind result in a linker failure akin to "cant find a QSORT that takes an Int4" or have the compiler widen/narrow the int to the appropriate type? I'm disappointed that it seems like its giving me undefined behaviour.

I needed to remind myself of how QSORT is defined. It's the first argument that changes type/kind depending on the signature. The second and third arguments are always SIZE_T. I note, though, that if you don't USE IFPORT, the compiler doesn't see explicit interfaces and can't warn you about any mismatches. If you do USE IFPORT, the compiler will not allow you to pass the wrong size integer for the len or isize arguments.

I'm also a little puzzled by the fortran communities lack of concern for a SIZE_T. Is this because multi-dimensional arrays alleviate that problem so well, its just assumed that for data structures in access of 4GB, you'll be able to fit them into a multi-dimensional array (eg matrix)? Its an interesting philososphy, and honestly I've always viewed the C++ need for an "index type" as a kind of clooge.

Fortran is generally designed so that you don't need to know the size of an address. It does start to break down a bit when you need to specify a size of an object that is outside the range of default integer, but C_SIZE_T is available and people use it.

Anyways, I have worked that specific problem; all invocations are updated to use an INTEGER(INT_PTR_KIND()). I'd like to provide a wrapper function for this, but I'm not sure on the parameter type for the `compar` comparator parameter. Does anybody have the QSORT fortran signature code?

You do. It's in IFPORT.F90 in the compiler's Include folder. The documentation explains the COMPAR function.

I'm at the point now where my production code can load the DLL, however it runs into a number of failures from the /checks:all. I've been working through those, but annoyingly, the stack-trace is always omitted with "Stack trace terminated abnormally."

You have stack corruption. Look carefully for mismatched calling conventions (STDCALL vs. C) on 32-bit.

 

 

0 Kudos
Groos__Geoff
Beginner
1,156 Views

You have stack corruption. Look carefully for mismatched calling conventions (STDCALL vs. C) on 32-bit.

my x86 (ia32) fortran dlls are able to generate stacks properly.

My x64 (amd64) ones are not. I would be a little surprised if the calling convention was the problem as, according to wikipedia, x64 is more standardized in its calling conventions than x86 was. Interestingly if I tell fortran to use stdcall I get unresolved BLAS symbol errors.

For a java application calling this fortran dll: jvm.dll is upstack and I presume that it is using the Windows x64 calling convention. I also presume ifort and the vs-linker are both doing the same.

So if calling convention isn't the issue then I presume I should be more concerned? Calling dll exports from java is messy business, I could easily see myself getting it wrong and clobbering some fence or padding that causes a validation error in fortran. Further, our DLL has never been entirely stable, it crashes randomly at the rate of maybe once per hundred execution hours. This could be related --though most of the debugging work here has been floating-point error related.

I needed to remind myself of how QSORT is defined. It's the first argument that changes type/kind depending on the signature. The second and third arguments are always SIZE_T. I note, though, that if you don't USE IFPORT, the compiler doesn't see explicit interfaces and can't warn you about any mismatches. If you do USE IFPORT, the compiler will not allow you to pass the wrong size integer for the len or isize arguments.

Excellent; exactly as you say, by putting USE IFPORT on the modules calling QSORT I get compiler errors when i call QSORT with the wrong int-length for size_t parameters.

I don't quite understand the facility fortran is using for the `USE` construct to _limit_ the amount of available symbols. Is this generalized? Are there a set of "best practice" USE statements to ensure maximum compiler strict-ness? I've got IMPLICIT NONE on every module.

Fortran is generally designed so that you don't need to know the size of an address. It does start to break down a bit when you need to specify a size of an object that is outside the range of default integer, but C_SIZE_T is available and people use it.

I've replaced INT_PTR_KIND() for C_SIZE_T because its a bit clearer and a bit more idiomatic. Any chance the fortran team could provide a builtin akin to `INT4(arg)` and `INT8(arg)` for `INT_C_SIZE_T(arg)`?

So my major work areas now are:

1. why is my stack corrupt? Can I get fortran to tell me about what is making it difficult to generate a stack trace? a useful avenue maybe be to create a c++ app that invokes some of the msvc that builds a stack trace --getting a second opinion at runtime if you will.
2. I want to change fortrans runtime error behaviour to leverage something like a exception-style long-jump. For all pure errors (divide by zero, FPE related, etc) this should be a reasonably effective recovery mechanism.
3. I want to change fortrans error behaviour to output somewhere other than standard-error. Ideally of course it would hit my logging framework at level SEVERE, but without some kind of general-purpose callback

0 Kudos
FortranFan
Honored Contributor II
967 Views

Groos, Geoff wrote:

.. Does anybody have the QSORT fortran signature code? ..

@Groos, Geoff:

Please note if you're referring to the C standard library function qsort, my recommendation will be to consume it in Fortran via a wrapper procedure as shown below.  Also, I'll suggest you consider using modern Fortran as much as possible given its considerable facilities in the language standard toward interoperability with C, particularly if you intend to use Fortran code in a mixed-language context with Java.  Modern Fortran allows you to have Fortran code that can masquerade as C code via bind(C,, ) language binding and then all you need to interoperate with Java, .NET, C++, etc. is the capabilities in these environments for interoperation with C and for which there is extensive information online.

module sort_m

   use, intrinsic :: iso_c_binding, only : c_int, c_double, c_size_t, c_ptr, c_funptr, c_loc, c_funloc
   use, intrinsic :: iso_fortran_env, only : character_storage_size

   private

   interface

      subroutine qsort( base, nitems, size, fcompare ) bind(C, name="qsort")
      ! Description
      ! The C library function qsort sorts an array.
      !
      ! Declaration
      ! void qsort(void *base, size_t nitems, size_t size,
      !            int (*compar)(const void *, const void*))
      ! Parameters
      ! base: pointer to the first element of the array to be sorted.
      ! nitems: the number of elements in the array pointed by base.
      ! size: the size in bytes of each element in the array.
      ! compar: function that compares two elements.
      !
      ! Return Value
      ! This function does not return any value.

         import :: c_ptr, c_size_t, c_funptr

         ! Argument list
         type(c_ptr), intent(in), value       :: base
         integer(c_size_t), intent(in), value :: nitems
         integer(c_size_t), intent(in), value :: size
         type(c_funptr), intent(in), value    :: fcompare

      end subroutine

   end interface

   interface sort ! elided are specific sort procedures for other types
      module procedure isort
      module procedure rsort
   end interface

   public :: sort

contains

   subroutine isort( a )

      ! Argument list
      integer(c_int), intent(inout), target :: a(:)

      ! Local variables
      integer(c_size_t) :: size_a
      integer(c_size_t) :: size_elem_a

      size_a = size(a, kind=c_size_t)
      size_elem_a = storage_size( a, kind=c_size_t )/character_storage_size

      call qsort( base=c_loc(a), nitems=size_a, size=size_elem_a, fcompare=c_funloc(icompare) )

   contains

      function icompare( x, y ) result(r) bind(C)

         ! Argument list
         integer(c_int), intent(in) :: x
         integer(c_int), intent(in) :: y
         ! Function result
         integer(c_int) :: r

         r = (x - y)

         return

      end function

   end subroutine

   subroutine rsort( a )

      ! Argument list
      real(c_double), intent(inout), target :: a(:)

      ! Local variables
      integer(c_size_t) :: size_a
      integer(c_size_t) :: size_elem_a

      size_a = size(a, kind=c_size_t)
      size_elem_a = storage_size( a, kind=c_size_t )/character_storage_size

      call qsort( base=c_loc(a), nitems=size_a, size=size_elem_a, fcompare=c_funloc(rcompare) )

   contains

      function rcompare( x, y ) result(r) bind(C)

         ! Argument list
         real(c_double), intent(in) :: x
         real(c_double), intent(in) :: y
         ! Function result
         integer(c_int) :: r

         r = 0
         if ( x < y ) then
            r = -1
         else if ( x > y ) then
            r = 1
         end if

         return

      end function

   end subroutine

end module
program p

   use, intrinsic :: iso_c_binding, only : c_int, c_double
   use sort_m, only : sort

   blk1: block

      integer(c_int), allocatable :: foo(:)

      print *, "Block 1: integer sort"

      foo = [ 42, 0, -1, -99, 100, 7, -50, 13, -10, 37 ]

      call sort( foo )

      print *, "foo(sorted) = ", foo

   end block blk1

   print *

   blk2: block

      real(c_double) :: foo(6)

      print *, "Block 2: sort array of floating-point values"

      call random_number( foo )

      call sort( foo )

      print *, "foo(sorted) = ", foo

   end block blk2

   stop

end program p

Upon execution of above code with Intel Fortran or for that matter with gfortran or another conforming compiler, the output will be along the following:

 Block 1: integer sort
 foo(sorted) =  -99 -50 -10 -1 0  7 13 37 42 100

 Block 2: sort array of floating-point values
 foo(sorted) =  3.920868194323862E-007 2.548044275764261E-002
 0.352516161261067 0.666914481524251 0.838288203465982
 0.963055531894656

You will thus notice this Fortran code using only standard Fortran features is essentially portable and agnostic to any platform-specific or compiler-specific considerations.  You can pretty much get the same behavior on Windows OS with Win32 or x64 target environment, or Linux, or with Intel Fortran or any other Fortran compiler.  The only requirement is a companion C processor and that is on account of your use of C library function qsort.

The main limitation currently in Fortran is inadequate support for GENERICs which leads to added developer time and code verbosity as can be seen with specific implementations for a 'sort' procedure of each type.  There are other options in Fortran with dummy arguments of unlimited polymorphic type (TYPE(*)/CLASS(*)) but they can lead to issues of their own, so I'm not showing any example with them here.

0 Kudos
Reply