Silverfrost Forums

Welcome to our forums

Linking C and FTN95 under Plato IDE

18 Dec 2014 5:13 #15224

I must be doing something wrong, but I cannot get the 'C' to link to the FTN95, no matter which calling sequence I use.

I've checked for other posts on this, but they all indicate that what I'm doing should work.

I have a large quantity of 'C++' code (in .cpp files) that I have compiled into a single library. There is one interfacing function that I will call from FTN95. I have the code snippets from the FTN 95 and C-source below, along with the linker message and the runtime error. The runtime shows that it cannot call _PDFCreate, yet the linker states that PDFCreate cannot be found. It would appear that SCC is not creating the linkage name that matches the FTN name, but I can find no options in SCC compiler to force a specific name.

I've tried STDCALL and changed the 'C' code to the __stdcall form, but no joy there either.

In the 'C' created .LIB file that I include as reference, PDCreate appears to have several 'incarnations' in naming, including '_PDFCreate__C' as well as 'PDFCreate'. I've tried all the logical variations, to no avail.

Any help is appreciated, Bill

In the FTN source: C_EXTERNAL PDFCREATE 'PDFCreate'(STRING) ... call PDFCREATE(TRIM(OPENED_FILENAME(I)))

In the 'C' source, compiled by SCC:

extern 'C' void PDFCreate(char *argv)

From the Link step: Compilation completed with no errors. Linking... WARNING the following symbols are missing: PDFCreate Z:\CMASTERF95\CheckMate\Win32\NEW_COPEN.obj (Z:\CMASTERF95\CFUNC\NEW_COPEN.FOR) Creating executable: CheckMate\Win32\C-MASTER.exe Creating Linker map file: map-c-master.map Linking completed.

Runtime error from program:z:\cmasterf95\checkmate\win32\c-master.exe Run-time Error *** Error 29, Call to missing routine : _PDFCreate at 0x00489bfa.

COPEN - in file new_copen.for at line 226 [+1d4f] [recur= 1]
LPCLOSE - in file lpset.for at line 80 [+00a0] CQUADB - in file cquadbld.for at line 242 [+21ad] XMMANT - in file cmaint.for at line 46 [+0551] main - in file cmain.for at line 89 [+0522]

18 Dec 2014 7:55 #15228

Just to try to keep moving forward, I included the C source in the project with the FTN files. Now, it properly finds the interface routine, just like it is supposed to. However, all the called routines now can't be found.

There must be a compile option that I am not setting, or that is not available and must be CONFIG'ed to work properly.

Any suggestions?

18 Dec 2014 8:19 #15229

If I run SLINK from the command line, and simply include all my .obj files in the link, then there are no un-found symbols. So, it has to be an option missing.

Where, and what, I'll need to change, I have no idea.

19 Dec 2014 3:17 #15233

So, this first trial at loading all the objects appeared to work, but didn't really.

I built a command file to load all the .obj files for the 4 projects that make up my build. Even though all the obj files are loaded, there are undefined symbols shown at link time. If I load the 'missing' obj file again, the linker complains (rightly so) that there are multiply defined symbols. It doesn't identify all the symbols, just the first one it encounters, then exist with an error. If I try to also link in the library created from the object files as well, there is no change in results.

So, I am at a complete loss now. Whatever is going on is subtle, and would appear to be some bug in either the SCC, FTN95, or SLINK or SLIM.

19 Dec 2014 9:14 #15234

Quoted from wahorger

I have a large quantity of 'C++' code (in .cpp files) that I have compiled into a single library.

Is this library compiled as DLL? If so, you only need to add a reference for the library DLL into Plato project. Just add required STDCALL or C_EXTERNAL statement into fortran code using those C-callable external functions and code should compile and link properly.

There is no need to use import libraries or handle name mangling manually, if you use Plato to build your project.

19 Dec 2014 2:40 #15236

jalih,

None of the libraries were compiled as DLL's (yet). I was hoping to make life simple; apparently that didn't happen!

I've no experience with building DLL's and perhaps this might be the time to learn.

Bill

22 Dec 2014 6:42 #15242

For your first experiment you should start by paswing a REAL or an INTEGER and not a STRING. Also pass the argument as a reference. Also use only upper case letters in the name. Use C_EXTERNAL and extern 'C'. You will find detailed instructions in ftn95.chm under 'mixed language programming'.

When it comes to STRINGS, every CHARCTER argument in Fortran, has a hidden length at the end which becomes visible in C. Again details can be found in ftn95.chm.

22 Dec 2014 7:16 #15249

I've worked out this particular problem, but it required me to move to the RELEASE compilation. (Apparently, I have a high tolerance for trying options until I find something that works.) I keep detailed records on what I try, and what I get as a result.

There are a couple of issues that are still affecting me between a CHECKMATE and RELEASE compilations. I am able to use the conditional compilation to mask the issue, but not yet solve it.

Bill

22 Dec 2014 7:27 #15251

If it doesn't work in CHECKMATE mode then there is something wrong that needs to be fixed.

22 Dec 2014 7:33 #15252

I'll try a very simple project with a few LOC and see if I can get it to replicate.

22 Dec 2014 7:52 #15253

Here is the test code, all in one FTN95 project (no separated projects/compiling)

First the FTN95. I used my existing calling sequence and call to the same same 'C' test code.

!FTN95 application...
     PROGRAM main
     c_EXTERNAL PDFCREATE 'PDFCreate'(STRING)
     CHARACTER*30 TESTSTRING
     DATA TESTSTRING/'THIS IS A TEST STRING'/
     CALL PDFCREATE(TESTSTRING)
     END PROGRAM main

Next, the 'C' code used to validate what I was doing.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern 'C' void PDFCreate(char *argv)
{
	char	*new_filename;

    printf('Source file: %s\\n',argv);
	new_filename = (char *)calloc(strlen(argv)+4,1);
	strcpy(new_filename,argv);
	strcat(new_filename,'.pdf');
    printf('Output file: %s\\n',new_filename);

	return;
}

FYI: For CHECKMATE, you have to select the Mangle option, for RELEASE, you must not.

The CHECKMATE version give the following error:

Runtime error from program:z:\cmasterf95\test\checkmate\win32\test.exe Access Violation The instruction at address 00401116 attempted to read from location f0f0f0f0 in file forttest.cpp at line 8

PDFCreate__C - in file forttest.cpp at line 8 [+0036] main - in file main.f95 at line 6 [+007f]

eax=ffffffff ebx=0019f4d4 ecx=f0f0f0f0 edx=0360fc54 esi=00401112 edi=0360fc70 ebp=0360fc68 esp=0360fc44 IOPL=2 ds=002b es=002b fs=0053 gs=002b cs=0023 ss=002b flgs=00010286 [NC EP NZ NS DN NV]

00401116 mov eax,[ecx] 00401118 cmp eax,0x80808080 0040111e jne 401125

The RELEASE compilation gives no error and performs as expected.

22 Dec 2014 9:47 #15255

The C code expects the string to be null terminated so one way forward is to concatenate your Fortran character variable with char (0). This would also require passing a REF rather than a STRING.

22 Dec 2014 10:09 #15256

From the HELP file ('Calling C/C++ from FTN95'):

If the function uses a string data type, code is planted to generate a C/C++ style string before entering the C/C++ function. Code is also generated after the function call to convert the C style string back into a Fortran string. This frees the C/C++ programmer from the additional complexities of providing the conversion code. It also means that a Fortran programmer can call a third party library without converting all string references into C/C++ strings before calling an external routine.

and

C_EXTERNAL COPY_STRING 'strcpy' (OUTSTRING,INSTRING): INTEGER4 C_EXTERNAL STRNCPY 'strcat' (STRING, INSTRING(40)): INTEGER4

where strcpy and strcat are the standard C library functions.

and those routines require null terminated 'strings'.

Therefore, my confusion.

I looked at the assembly code, and in the CHECKMATE and RELEASE compilation, there is a call to MAKE_CSTRING@ prior to the PDFCreate call, then the PDFCreate call, then a call to GET_CSTRING@ after the call. There are subtle differences in the assembly code between CHECKMATE and RELEASE compilations. [/quote]

22 Dec 2014 10:17 #15257

Also, terminating the FORTRAN with a NULL string yielded the same error.

 CHARACTER*30 TESTSTRING
 character*1 NULLSTRING
 NULLSTRING = char(0)
 DATA TESTSTRING/'THIS IS A TEST STRING'/
 CALL PDFCREATE(TESTSTRING//NULLSTRING)
23 Dec 2014 10:48 #15258

Quoted from wahorger

Next, the 'C' code used to validate what I was doing.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern 'C' void PDFCreate(char *argv)
{
  char	*new_filename;

    printf('Source file: %s\\n',argv);
  new_filename = (char *)calloc(strlen(argv)+4,1);
  strcpy(new_filename,argv);
  strcat(new_filename,'.pdf');
    printf('Output file: %s\\n',new_filename);

  return;
}

There is an error in your C-code: you are allocating buffer for the filename + .pdf file extension, but forgot to add one byte for the null.

Access violation comes, when trying to write outside the buffer.

By the way, I think you can add a null-byte for the fortran string by just assigning:

character(20) :: str

str = 'This is C-string.'C
23 Dec 2014 11:40 #15259

I have had a quick look at this and this is as far as I have got. You can ignore most of my comments above. Your code is basically OK. You should write your C code and test it from a C main program using CHECKMATE but do not use CHECKMATE (for the C code) when accessing C from Fortran.

FTN95 adds one or more hidden arguments (and/or it modifies the assembler) when using CHECKMATE and these changes are probably causing the failure. The name of the function becomes PDFCreate__C but this change alone is not sufficient. You need to know the form of the hidden argument(s) etc and this is undocumented.

You can use /DEBUG for the C and /CHECKMATE for the Fortran (whilst testing) if this helps. Naturally release mode is always used after testing.

In situations where you need to add a null terminator in the Fortran then it usually requires trim(var)//char(0).

23 Dec 2014 9:12 #15260

Good catch jalih!

Not the source of the crash, but a good catch! I'll update that ASAP.

The issue with the actual code (not this test case) is that the filename strinc is not statically defined. It can be any name, or any length. And, as the documentation so states, one shouldn't have to add a NULL to the string in the FTN code. Itr is supposed to automatic.

And, why does it fail in CHECKMATE, yet work in RELEASE? Makes no sense (yet!).

23 Dec 2014 9:16 #15261

Paul, thanks for the reply.

I really hate to have conditional compilation to support what should be transparent to the code, so will look into what I might be able to do to get past the inconsistency. The usage of REF might work, but will surely require changes in the calling sequence (which, I guess, I am willing to do).

23 Dec 2014 10:18 #15262

I think that you may not have understood my last post.

As I understand it you don't need to change anything. Just avoid using /CHECKMATE or /CHECK when compiling the C code.

23 Dec 2014 10:25 #15263

Paul, thanks for the additional explanation. I will investigate that approach.

Thanks, Bill

Please login to reply.