- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Now I'm trying to return a string from C to Fortran. This is a follow on to the following thread. I'm using the same codes, just modifying them to also pass back a string as well as an integer.
http://software.intel.com/en-us/forums/showthread.php?t=62417
After a fair amount of googling I finally ran across this link:
http://docs.hp.com/en/B3909-90014/ch08s05.html
Before I added the "fix_string_for_f90" stuff to the C code I was able to build/link and run, but just as in the integer case, nothing showed up on the Fortran side for "output_text", just a blank 80 character string.
When I compile the modified C code I get this warning for line 49 ("for (i=strlen(s)...):
"warning C4267: '=' : conversion from 'size_t' to 'int', possible loss of data"
Googling seems to indicate that this is related to 32bit vs 64bit systems and that it can be ignored on 32 bit systems but I'm not sure.
But it also breaks on the last line of the C code ("s =' ';") with an unhandled exception.
I'm stuck. But I know I have to be real close.
I'm using Visual Studio 2005 (with sp1) and IVF 11.0.
Mike
Fortran Code
[cpp]! Copyright (C) 2008 Intel Corporation. All Rights Reserved. ! PROGRAM READDATAFILE IMPLICIT NONE ! This is an example of a Fortran main program calling ! a C routine. It uses the C Interoperability features ! of Fortran 2003 ! Declare the interface for the C routine we'll call ! INTERFACE ! The BIND(C) tells the compiler that this is an "interoperable" ! procedure. The compiler adjusts the naming conventions as ! appropriate for the companion C processor. SUBROUTINE c_routine (int_arg, str_in, str_out) BIND(C) c SUBROUTINE c_routine (int_arg) BIND(C) USE,INTRINSIC :: ISO_C_BINDING ! Declares C kinds ! First argument is a C "int", passed by value c INTEGER(C_INT), VALUE,INTENT(IN) :: int_arg ! Second and third arguments are C "char *", represented ! in Fortran by an array of single characters of kind C_CHAR. ! Note that the language allows passing a regular CHARACTER ! variable to such an argument. CHARACTER(KIND=C_CHAR),DIMENSION(*) :: str_in,str_out END SUBROUTINE c_routine END INTERFACE c INTEGER int_arg c COMMON /int_arg/int_arg CHARACTER(80) OUTPUT_TEXT INTEGER IN_ARG, OUTPUT_LEN CHARACTER(80) INPUT_TEXT INPUT_TEXT = "Testing..."C ! C suffix adds a null terminator IN_ARG = 123 ! Call c_routine. It will return text in OUTPUT_TEXT ! CALL c_routine (in_arg, input_text, output_text) c CALL c_routine (in_arg) ! Find the length of the output text, looking ! for the trailing blank ! c OUTPUT_LEN = INDEX(OUTPUT_TEXT," ") c IF (OUTPUT_LEN == 0) OUTPUT_LEN = 80 ! Write the string to the console ! c WRITE (*,*) OUTPUT_TEXT(1:OUTPUT_LEN) END[/cpp]
C Code
[cpp]/* ! Copyright (C) 2007 Intel Corporation. All Rights Reserved. ! */ /* C routine called by Fortran main program ** ** Converts integer input argument to text, appends ** the text to string input argument and stores the ** result in the string output argument */ #include#include #define DllExport __declspec( dllexport ) void fix_string_for_f90(output_text, slen); //extern int int_arg; extern output_text; __declspec( dllexport ) c_routine(); extern c_routine ( int *int_arg, char* input_text, char* output_text, int slen ) //extern c_routine (int *int_arg) { output_text = "Hi there"; *int_arg = 2; fix_string_for_f90(output_text, slen); // sprintf(output_text,"%s%i ",input_text,int_arg); //return 0; } void fix_string_for_f90(char s[], int len) { int i; for (i = strlen(s); i < len; i++) s = ' '; }[/cpp]
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your argument lists don't match on the Fortran and C side. There appear to be other variable name spelling errors.
How would that C on the end of the string take the place of //C_NULL_CHAR ?
As the warning tells you, strlen() has type size_t. As you wouldn't want that large a string here, what you have done with that looks harmless; if you string isn't null terminated, you'll get garbage anyway.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your argument lists don't match on the Fortran and C side. There appear to be other variable name spelling errors.
How would that C on the end of the string take the place of //C_NULL_CHAR ?
As the warning tells you, strlen() has type size_t. As you wouldn't want that large a string here, what you have done with that looks harmless; if you string isn't null terminated, you'll get garbage anyway.
Most of the code was not written by me. Both the original Fortran and C codes are from the Fortran_Calls_C example folder in IVF 11.0. The "fix_string_for_f90" code is from the hp website I referred to in the beginning of my post, it wasn't something I had done. I am not familiar with C, although I am trying to become familiar with it. Are you sure you are not confusing the commented out calls with the calls that are actually being used? Perhaps I should have removed the commented ones. Could you be more specific? Thanks.
Mike
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A modified set of fortran and c-code below. I normally only play on the C++ swings, so apologies if the C code is a bit out. I'm no interoperability expert either but have been looking at this recently. I appreciate that this code is borrowed from an example, but some points:
On the Fortran side
- Consider using implicit none in the interface - it would identify that there's no declaration for int_arg (as specified things are fine because your passing by reference, but having the commented out VALUE declaration in there is very confusing).
- I believe the 'xxx'C thing is an IVF extension which works as you want, but if you want to be standard compliant use 'xxx' // C_NULL_CHAR
- Looking for end of string spaces with INDEX (in commented out code) can result in issues if your actual string contains spaces. LEN_TRIM and TRIM avoid this problem and are concise and readable. Alternatively you could explicitly pass the length back (as demonstrated) or go looking for the terminating NULL that the C library functions add.
- Use of F77 source form is a bit incongruous with the use of F2003 features.
On the C side
- the size_t to int warning is probably driven by unsigned/signed type issues. You can ignore it unless your strings are big. Really big.
- you need to copy the "Hi there" literal string using strcpy or similar, otherwise you are just assigning the address of the literal to something that just goes out of scope at the end of the function anyway. This is also the cause of your access violation - as written you are trying to blank out the end of the literal string constant, which isn't stored in its own 80 character buffer and might even be in "read only" memory.
Overall
- To be robust, I'd explicitly pass buffer sizes (eg. LEN(xx)) and check that I wasn't going to obliterate something by writing beyond the end of the buffer. IVF passes the length of character scalars as additional arguments, but I think that's an extension and, as my example demonstrates, can be misleading. Note that slen in your code is the length of the input buffer, but you use it as if it is the length of the output buffer.
- Your Fortran interface specifies a SUBROUTINE. Your C function is (by omission) declared as returning an int, which is more akin to a fortran INTEGER FUNCTION (you're then not specifying what the return is... so C would probably return garbage anyway). The original C example is declared void, which is more appropriate and technically required for a SUBROUTINE.
IanH
[cpp]PROGRAM FortranCStrings USE,INTRINSIC :: ISO_C_BINDING ! Declares C kinds IMPLICIT NONE ! Declare the interface for the C routine we'll call INTERFACE SUBROUTINE c_routine_v2( int_by_val, int_by_ref, str1, str2, str3, & str4 ) BIND(C) IMPORT C_INT, C_CHAR ! or USE ... :: ISO_C_BINDING again IMPLICIT NONE ! First argument is a C "int", passed by value INTEGER(C_INT), VALUE, INTENT(IN) :: int_by_val ! Second argument is a C "int", passed by reference (default) INTEGER(C_INT), INTENT(IN) :: int_by_ref ! Remaining arguments are C "char *", either fortran arrays of single ! characters or normal character scalars of whatever length CHARACTER(KIND=C_CHAR), DIMENSION(*) :: str1, str2, str3, str4 END SUBROUTINE c_routine_v2 END INTERFACE ! Main program manages the storage for the strings ! CHARACTER scalars (but LEN /= 1) CHARACTER(LEN=80,KIND=C_CHAR) :: text1, text3, text4 ! CHARACTER array (LEN == 1) CHARACTER(LEN=1,KIND=C_CHAR) :: text2a(80) INTEGER :: text4_len INTEGER :: int_v, int_r INTEGER :: i !***************************************************************************** ! IVF extension? (I get no warning but can't find in std) - null gets ! automatically appended !text1 = "Testing..."C ! "Robust" F2003 compliant approach text1 = C_CHAR_"Testing..." // C_NULL_CHAR ! build character array, a little painfully... text2a(1:9) = [ 'm', 'y', ' ', 'a', 'r', 'r', 'a', 'y', C_NULL_CHAR ] ! Nonsense values so we can see what they look like on the other side int_v = 123 int_r = 456 ! Call c_routine. It will put things in text3 and text4, and the resulting ! length of text3 into int_r. I suspect warning #7262 here is spurious given ! the example in Note 15.23. CALL c_routine_v2(int_v, int_r, text1, text2a, text3, text4) WRITE(*,"(A)") 'Hello from the main program' ! For text3, the "C" length has been put into int_r. C will have NULL ! terminated the string, but Fortran doesn't understand - so this might ! show rubbish beyond the intended end of the string. WRITE(*,100) 'text3 (i)', TRIM(text3), LEN_TRIM(text3), int_r ! Fix the strip up by chopping off the null and other rubbish beyond the ! end. Fortran does this by blanking the remainder of the string to spaces. text3 = text3(1:int_r) ! another possibility - doesn't require returned length !text3 = text3(1:INDEX(text3, C_NULL_CHAR)-1) WRITE(*,100) 'text3 (ii)', TRIM(text3), LEN_TRIM(text3), int_r ! for text4, we can use LEN_TRIM to find where all the spaces begin. ! INDEX(text4, ' ') would return the location of the first space, which might ! not be at the end text4_len = LEN_TRIM(text4) WRITE (*,100) 'text4 (i)', text4(1:text4_len), text4_len ! equivalent to... WRITE (*,100) 'text4 (ii)', TRIM(text4), text4_len 100 FORMAT(A30,': "',A,'"',:,', LEN_TRIM is ',I0,:,', "C" length is ',I0) END PROGRAM FortranCStrings[/cpp]
[cpp]#include#include /* Declare worker routine, including argument types */ void fix_string_for_f90(char output_text[], size_t slen); /* Declare and define function */ __declspec( dllexport ) void c_routine_v2( int int_by_val, int* int_by_ref, char* str1, char* str2, char* str3, char* str4, int ivf_str1_len, int ivf_str2_len, int ivf_str3_len, int ivf_str4_len ) { /* Show inputs */ printf("Hello from c_routinen"); printf("%30s: %dn", "int_by_val", int_by_val); printf("%30s: %dn", "*int_by_ref", *int_by_ref); printf("%30s: "%s"n", "str1", str1); printf("%30s: "%s"n", "str2", str2); /* str3 and str4 weren't initialised by fortran, so we'll skip them */ /* I believe the following are IVF extensions (not std?) - extra parameters containing string lengths. ivf_str2_len ends up being 1, not 80, because the actual argument is an array. */ printf("%30s: %dn", "ivf_str1_len", ivf_str1_len); printf("%30s: %dn", "ivf_str2_len", ivf_str2_len); printf("%30s: %dn", "ivf_str3_len", ivf_str3_len); printf("%30s: %dn", "ivf_str4_len", ivf_str4_len); /* Copy the string literal into the memory pointed to by str3. If the literal (including terminating NULL) is longer than the buffer pointed to by str3 (longer than LEN(text1)) then bad things will happen. (Using str3="xx"; doesn't actually copy the literal string - just it's address, and that address doesn't make its way back to the calling routine anyway) */ strcpy(str3, "Hi there"); /* Return the length of that string literal in int_by_ref. Use a cast to avoid warnings about putting unsigned values where they technically might not fit. */ *int_by_ref = (int) strlen(str3); /* Put something a little more complicated in str4. If the total length of the string (including terminating NULL) is more than the length of the buffer pointed to by str4, then bad things will happen. */ sprintf(str4, "%s %d", str1, int_by_val); /* Fill the buffer with spaces. To be robust the length of the str4 buffer should have been passed as an extra parameter and used here */ fix_string_for_f90(str4, 80 /* ivf_str4_len */ ); } /* Define worker routine to blank out string buffer. * Note that if you try and use the string in C library functions * after this, then bad things will happen because you obliterate the * terminating NULL. */ void fix_string_for_f90(char s[], size_t len) { size_t i; /* Avoid size_t != int warnings */ for (i = strlen(s); i < len; i++) s = ' '; } [/cpp]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A modified set of fortran and c-code below. I normally only play on the C++ swings, so apologies if the C code is a bit out. I'm no interoperability expert either but have been looking at this recently. I appreciate that this code is borrowed from an example, but some points:
On the Fortran side
- Consider using implicit none in the interface - it would identify that there's no declaration for int_arg (as specified things are fine because your passing by reference, but having the commented out VALUE declaration in there is very confusing).
- I believe the 'xxx'C thing is an IVF extension which works as you want, but if you want to be standard compliant use 'xxx' // C_NULL_CHAR
- Looking for end of string spaces with INDEX (in commented out code) can result in issues if your actual string contains spaces. LEN_TRIM and TRIM avoid this problem and are concise and readable. Alternatively you could explicitly pass the length back (as demonstrated) or go looking for the terminating NULL that the C library functions add.
- Use of F77 source form is a bit incongruous with the use of F2003 features.
On the C side
- the size_t to int warning is probably driven by unsigned/signed type issues. You can ignore it unless your strings are big. Really big.
- you need to copy the "Hi there" literal string using strcpy or similar, otherwise you are just assigning the address of the literal to something that just goes out of scope at the end of the function anyway. This is also the cause of your access violation - as written you are trying to blank out the end of the literal string constant, which isn't stored in its own 80 character buffer and might even be in "read only" memory.
Overall
- To be robust, I'd explicitly pass buffer sizes (eg. LEN(xx)) and check that I wasn't going to obliterate something by writing beyond the end of the buffer. IVF passes the length of character scalars as additional arguments, but I think that's an extension and, as my example demonstrates, can be misleading. Note that slen in your code is the length of the input buffer, but you use it as if it is the length of the output buffer.
- Your Fortran interface specifies a SUBROUTINE. Your C function is (by omission) declared as returning an int, which is more akin to a fortran INTEGER FUNCTION (you're then not specifying what the return is... so C would probably return garbage anyway). The original C example is declared void, which is more appropriate and technically required for a SUBROUTINE.
IanH
[cpp]PROGRAM FortranCStrings
USE,INTRINSIC :: ISO_C_BINDING ! Declares C kinds
IMPLICIT NONE
! Declare the interface for the C routine we'll call
INTERFACE
SUBROUTINE c_routine_v2( int_by_val, int_by_ref, str1, str2, str3, &
str4 ) BIND(C)
IMPORT C_INT, C_CHAR ! or USE ... :: ISO_C_BINDING again
IMPLICIT NONE
! First argument is a C "int", passed by value
INTEGER(C_INT), VALUE, INTENT(IN) :: int_by_val
! Second argument is a C "int", passed by reference (default)
INTEGER(C_INT), INTENT(IN) :: int_by_ref
! Remaining arguments are C "char *", either fortran arrays of single
! characters or normal character scalars of whatever length
CHARACTER(KIND=C_CHAR), DIMENSION(*) :: str1, str2, str3, str4
END SUBROUTINE c_routine_v2
END INTERFACE
! Main program manages the storage for the strings
! CHARACTER scalars (but LEN /= 1)
CHARACTER(LEN=80,KIND=C_CHAR) :: text1, text3, text4
! CHARACTER array (LEN == 1)
CHARACTER(LEN=1,KIND=C_CHAR) :: text2a(80)
INTEGER :: text4_len
INTEGER :: int_v, int_r
INTEGER :: i
!*****************************************************************************
! IVF extension? (I get no warning but can't find in std) - null gets
! automatically appended
!text1 = "Testing..."C
! "Robust" F2003 compliant approach
text1 = C_CHAR_"Testing..." // C_NULL_CHAR
! build character array, a little painfully...
text2a(1:9) = [ 'm', 'y', ' ', 'a', 'r', 'r', 'a', 'y', C_NULL_CHAR ]
! Nonsense values so we can see what they look like on the other side
int_v = 123
int_r = 456
! Call c_routine. It will put things in text3 and text4, and the resulting
! length of text3 into int_r. I suspect warning #7262 here is spurious given
! the example in Note 15.23.
CALL c_routine_v2(int_v, int_r, text1, text2a, text3, text4)
WRITE(*,"(A)") 'Hello from the main program'
! For text3, the "C" length has been put into int_r. C will have NULL
! terminated the string, but Fortran doesn't understand - so this might
! show rubbish beyond the intended end of the string.
WRITE(*,100) 'text3 (i)', TRIM(text3), LEN_TRIM(text3), int_r
! Fix the strip up by chopping off the null and other rubbish beyond the
! end. Fortran does this by blanking the remainder of the string to spaces.
text3 = text3(1:int_r)
! another possibility - doesn't require returned length
!text3 = text3(1:INDEX(text3, C_NULL_CHAR)-1)
WRITE(*,100) 'text3 (ii)', TRIM(text3), LEN_TRIM(text3), int_r
! for text4, we can use LEN_TRIM to find where all the spaces begin.
! INDEX(text4, ' ') would return the location of the first space, which might
! not be at the end
text4_len = LEN_TRIM(text4)
WRITE (*,100) 'text4 (i)', text4(1:text4_len), text4_len
! equivalent to...
WRITE (*,100) 'text4 (ii)', TRIM(text4), text4_len
100 FORMAT(A30,': "',A,'"',:,', LEN_TRIM is ',I0,:,', "C" length is ',I0)
END PROGRAM FortranCStrings[/cpp]
[cpp]#include
#include
/* Declare worker routine, including argument types */
void fix_string_for_f90(char output_text[], size_t slen);
/* Declare and define function */
__declspec( dllexport ) void c_routine_v2(
int int_by_val,
int* int_by_ref,
char* str1,
char* str2,
char* str3,
char* str4,
int ivf_str1_len,
int ivf_str2_len,
int ivf_str3_len,
int ivf_str4_len )
{
/* Show inputs */
printf("Hello from c_routinen");
printf("%30s: %dn", "int_by_val", int_by_val);
printf("%30s: %dn", "*int_by_ref", *int_by_ref);
printf("%30s: "%s"n", "str1", str1);
printf("%30s: "%s"n", "str2", str2);
/* str3 and str4 weren't initialised by fortran, so we'll skip them */
/* I believe the following are IVF extensions (not std?) - extra parameters
containing string lengths. ivf_str2_len ends up being 1, not 80, because
the actual argument is an array. */
printf("%30s: %dn", "ivf_str1_len", ivf_str1_len);
printf("%30s: %dn", "ivf_str2_len", ivf_str2_len);
printf("%30s: %dn", "ivf_str3_len", ivf_str3_len);
printf("%30s: %dn", "ivf_str4_len", ivf_str4_len);
/* Copy the string literal into the memory pointed to by str3. If the literal
(including terminating NULL) is longer than the buffer pointed to by str3
(longer than LEN(text1)) then bad things will happen. (Using str3="xx";
doesn't actually copy the literal string - just it's address, and that
address doesn't make its way back to the calling routine anyway) */
strcpy(str3, "Hi there");
/* Return the length of that string literal in int_by_ref. Use a cast
to avoid warnings about putting unsigned values where they technically
might not fit. */
*int_by_ref = (int) strlen(str3);
/* Put something a little more complicated in str4. If the total length
of the string (including terminating NULL) is more than the length of
the buffer pointed to by str4, then bad things will happen. */
sprintf(str4, "%s %d", str1, int_by_val);
/* Fill the buffer with spaces. To be robust the length of the str4 buffer
should have been passed as an extra parameter and used here */
fix_string_for_f90(str4, 80 /* ivf_str4_len */ );
}
/* Define worker routine to blank out string buffer.
* Note that if you try and use the string in C library functions
* after this, then bad things will happen because you obliterate the
* terminating NULL.
*/
void fix_string_for_f90(char s[], size_t len)
{
size_t i; /* Avoid size_t != int warnings */
for (i = strlen(s); i < len; i++) s = ' ';
} [/cpp]
Thanks a bunch Ian! I was able to get these to codes to compile, link and run. However, FYI, I do get the two warnings below when I build the C code:
1>c:testarrayianc.c(41) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:program filesmicrosoft visual studio 8vcincludestring.h(74) : see declaration of 'strcpy'
1>c:testarrayianc.c(50) : warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:program filesmicrosoft visual studio 8vcincludestdio.h(345) : see declaration of 'sprintf'
I will need some time to absorb this and see if I can incorporate what I need into my application code.
I have just one more hurdle to get over and perhaps I should have mentioned this before. I need to also pass double precision two - dimensional arrays from the C side to Fortran. Is there anyway you could incorporate this into your example? It believe it should be much easier than passing strings. I have actually done all this using Fortran and Excel, i.e. passing integers, reals, strings (ughh!) and arrays back and forth between Excel (VBA) and Fortran and I know that when passing an array to Fortran from VBA you just pass the first element of the array. I can't figure out how to do this with C though. (I'm the newest of newbies at C although I'm learning. Learning to despise this pointer stuff that is :) :) )
Again, I'm using Visual Studio 2005 with SP1 and a Vista related Visual Studio 2005 upgrade and IVF 11.0
Mike
If you (or anyone else) would like some background/context you can read on: (kind of long, only for those interested)
I'm an operations test engineer at a NASA field center. We use something called Labview to collect data from a test cell that operates small rocket engines. In one (older) test cell I pretty much took over the data crunching fortran program as our computer support gradually eroded away. Something I actually enjoy doing but it's a bit low on the prioirty list as an ops engineer. I have an analytical background and have some (very outdated actually) Fortran programming experience. In the older test cell, we have an older version of labview which outputs a binary file which is easily read by Fortran using a "...format = binary" option in a read statement. No problemo.
(Yea I know, everyone's wondering why I can't get help at work, this being NASA and all. Not that simple. First, the computer division's been decimated over the last decade or so. There's also the issue of finding someone with time and with full-cost accounting there's the issue with money, although I am going to try and ask around again.)
We now are trying to get another,very similar, test cell up and running and it uses the latest version of labview which outputs data into a proprietary form called TDMS. Can't read this directly from Fortran. The company (National Instruments, NI) sells a product (DIAdem) that reads these files and can produce fancy charts, however, I use the Fortran code and excel to costumize what I need to do with the data. I would like to adapt my Fortran code for use in the new test cell.
So....it turns out that someone at NI wrote a C/C++ code that shows how to read their TDMS files. It comes with a library that contains "DDC" routines that extract the data. What I'm trying to do is get this data from the C/C++ code to my "legacy" Fortran code. Here's the NI C/C++ code (all it does is read text information, read data column headings, read the data one column at a time then averages the data in each column and prints this information to the console):
(Hmmm. I just pasted the code in using the "pencil" tool and I'm not getting any line numbers on the left-hand side. Are those put in when I post or did I not paste correctly? I was hoping to use those line numbers to reference specific lines. Oh well.)
[cpp]//-----------------------------------------------------------------------------
//
// This sample program shows how to read a DIAdem file.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "nilibddc.h"
#include
#include
#define Dllimport __declspec( dllimport ) //MJB
#define DllExport __declspec( dllexport ) //MJB
//#define _main main //MJB
DllExport MAIN(); //MJB
extern MAIN(); //MJB Doing this allows debugging from FortRead to go into CDLL
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
#define ddcChk(f) if (ddcError = (f), ddcError < 0) goto Error; else
#ifdef nullChk
#undef nullChk
#endif
#define nullChk(p) if (!(p)) { ddcError = DDC_OutOfMemory; goto Error; } else
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
//static const char * FILE_PATH = "testtdms.tdms";
static const char * FILE_PATH = "C:ACS11_3_2008_2_57.tdms";
//static const char * FILE_PATH = "C:ACS2_6_2008_9_58 AM.tdms";
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
static int ReadFile (void);
static int ReadGroups (DDCFileHandle file);
static int ReadChannels (DDCChannelGroupHandle group);
double GetAvgDataValue (unsigned __int64 numDataValues, double *data);
#define cols 500 //MJB
#define rows 90000 //MJB
double array2[cols][rows]; //MJB
int count; //MJB
struct mystuff{double array2;}; //MJB An attempt to interface with a common block in Fortran
//-----------------------------------------------------------------------------
// Program entry-point function
//-----------------------------------------------------------------------------
//int MAIN (int argc, char *argv[]) //Original commented out by MJB
//int MAIN (int *argc) //MJB
int MAIN (double *array2, int *argc) //MJB
{
int ddcError = 0;
*argc = 2; //MJB Simply added in attempt to pass an integer. Not integral to original code
ddcChk (ReadFile());
Error:
if (ddcError < 0)
printf ("nError: %sn", DDC_GetLibraryErrorDescription(ddcError));
else
printf ("nNo errors.n");
printf("End of program, press Enter key to quitn");
getchar();
return 0;
}
//-----------------------------------------------------------------------------
// Helper functions
//-----------------------------------------------------------------------------
// Reads the file
static int ReadFile (void)
{
int ddcError = 0;
unsigned int length;
DDCFileHandle file = 0;
char *property = 0;
// Read file name
ddcChk (DDC_OpenFile (FILE_PATH, "TDMS", &file));
ddcChk (DDC_GetFileStringPropertyLength (file, DDC_FILE_NAME, &length));
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetFileProperty (file, DDC_FILE_NAME, property, length + 1));
// printf ("File name property: %sn", property); //MJB Don't need for my app
free (property);
property = 0;
// Read file description, if present
if (DDC_GetFileStringPropertyLength (file, DDC_FILE_DESCRIPTION, &length) >= 0)
{
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetFileProperty (file, DDC_FILE_DESCRIPTION, property, length + 1));
// printf ("File description property: %sn", property); //MJB Don't need for my app
free (property);
property = 0;
}
// Read the channel groups
ddcChk (ReadGroups (file));
Error:
if (property)
free (property);
if (file)
DDC_CloseFile (file);
return ddcError;
}
// Reads all the channel groups in a file
static int ReadGroups (DDCFileHandle file)
{
int ddcError = 0;
unsigned int length;
unsigned int i, numGroups;
DDCChannelGroupHandle *groups = 0;
char *property = 0;
// Get all the channel groups
ddcChk (DDC_GetNumChannelGroups (file, &numGroups));
nullChk (groups = (DDCChannelGroupHandle *)calloc (numGroups, sizeof (DDCChannelGroupHandle)));
ddcChk (DDC_GetChannelGroups (file, groups, numGroups));
for (i = 0; i < numGroups; ++i)
{
// Read the channel group name
ddcChk (DDC_GetChannelGroupStringPropertyLength (groups, DDC_CHANNELGROUP_NAME,
&length));
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetChannelGroupProperty (groups, DDC_CHANNELGROUP_NAME,
property, length + 1));
printf ("n");
printf ("Channelgroup #%d name property: %sn", i+1, property);
free (property);
property = 0;
// Read the channel group description, if present
if (DDC_GetChannelGroupStringPropertyLength (groups,
DDC_CHANNELGROUP_DESCRIPTION, &length) >= 0)
{
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetChannelGroupProperty (groups, DDC_CHANNELGROUP_DESCRIPTION,
property, length + 1));
printf ("Channelgroup #%d description property: %sn", i+1, property);
free (property);
property = 0;
}
// Read the channels in this group
ddcChk (ReadChannels (groups));
}
Error:
// Cleanup
if (groups)
free (groups);
if (property)
free (property);
return ddcError;
}
// Reads all the channels in a channel group
static int ReadChannels (DDCChannelGroupHandle group)
{
int ddcError = 0;
unsigned int i, numChannels, length;
unsigned __int64 numDataValues;
DDCChannelHandle *channels = 0;
char *property = 0;
double *data = 0; //avgDataValue;
// Read all the channels in this channel group
ddcChk (DDC_GetNumChannels (group, &numChannels));
nullChk (channels = (DDCChannelHandle *) calloc (numChannels, sizeof (DDCChannelHandle)));
ddcChk (DDC_GetChannels (group, channels, numChannels));
for (i = 0; i < numChannels; ++i)
{
// Read the channel name
ddcChk (DDC_GetChannelStringPropertyLength (channels, DDC_CHANNEL_NAME, &length));
nullChk (property = (char *) malloc (length + 1));
ddcChk (DDC_GetChannelProperty (channels, DDC_CHANNEL_NAME, property, length + 1));
printf ("n");
printf ("Channel #%d name property: %sn", i+1, property);
free (property);
property = 0;
// Read the channel description, if present
if (DDC_GetChannelStringPropertyLength (channels, DDC_CHANNEL_DESCRIPTION, &length) >= 0)
{
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetChannelProperty (channels, DDC_CHANNEL_DESCRIPTION,
property, length + 1));
printf ("Channel #%d description property: %sn", i+1, property);
free (property);
property = 0;
}
// Read the channel units, if present
if (DDC_GetChannelStringPropertyLength (channels, DDC_CHANNEL_UNIT_STRING, &length) >= 0)
{
nullChk (property = (char *)malloc (length + 1));
ddcChk (DDC_GetChannelProperty (channels, DDC_CHANNEL_UNIT_STRING,
property, length + 1));
printf ("Channel #%d unit string property: %sn", i+1, property);
free (property);
property = 0;
}
// Read the channel data
ddcChk (DDC_GetNumDataValues (channels, &numDataValues));
nullChk (data = (double *) malloc (sizeof (double) * (unsigned int)numDataValues));
ddcChk (DDC_GetDataValues (channels, 0, (unsigned int)numDataValues, data));
for (count =1; count<=numDataValues; count++) //MJB
array2[count] = data[count]; //MJB
// avgDataValue = GetAvgDataValue (numDataValues, data); //MJB Orig code, don't need
// printf ("Channel #%d number of data values: %I64dn", i+1, numDataValues); //MJB Orig code, don't need
// printf ("Channel #%d average data value: %.2fn", i+1, avgDataValue); //MUB " " " "
}
Error:
// Cleanup
if (data)
free (data);
if (channels)
free (channels);
if (property)
free (property);
return ddcError;
}
double GetAvgDataValue (unsigned __int64 numDataValues, double *data)
{
int i;
double sum = 0.0;
for (i = 0; i < numDataValues; i++)
sum += data;
return sum / (unsigned int)numDataValues;
}
[/cpp]
The most critical line for me is the one that ends "...numDataValues, data));" located about 20 to 25 lines up from the bottom, above a //MJB commented line. "data" is a 1-dimensional array that contains one column of data. The code loops through reading each column one at a time, averages the data, prints out various information, then erases memory and does the same thing for the next column of data. I've added a couple of lines after this in order to store the data in a 2-dim array named "array2" that will contain all the columns of data and needs to be passed on to the Fortran code. I have some "//MJB" commented lines near the top that declare this array. (I just went back and "bolded" these lines.) Remember, I am very new to C/C++. I've got this to run with the Fortran below without errors, but the array on the Fortran side remains empty (all zero's as far as I can tell).
Here's the Fortran subroutine that I've temporarily turned into and main and stripped down that calls the above code:
(You can see that I've tried using an interface block, although for some reason I can't think of right now I have it commented out. This was before I upgraded to 11.0 and discovered the 2003 interoperability extensions which I haven't had too much time too play with yet. I also had a common block that was used in conjuction with the "struct" line in the C code which I erased at some point.)
[cpp]C********************************************************************
C
C SUBROUTINE READDATAFILE.for
C
C This subroutine reads the data file and stores the information
C in the QAEU Common
C
C********************************************************************
Program READDATAFILE
c implicit none
c INTERFACE
c SUBROUTINE MAIN (int_arg, str_in)
c SUBROUTINE MAIN (int_arg)
! Specify C calling and naming conventions
c !DEC$ ATTRIBUTES C :: MAIN
c !DEC$ ATTRIBUTES :: int_arg
c INTEGER, INTENT(IN) :: int_arg
c INTEGER :: int_arg
cc CHARACTER(*) str_in
!
! Specify that the CHARACTER arguments are by reference
! with no hidden length
cc !DEC$ ATTRIBUTES REFERENCE :: str_in
c END SUBROUTINE MAIN
c END INTERFACE
CHARACTER(80) OUTPUT_TEXT,str_in,str_out
INTEGER*4 INT_ARG, OUTPUT_LEN
CHARACTER(80) INPUT_TEXT
real*8 DATASTUFF(90000,500)
int_arg = 123
Call MAIN(datastuff, int_arg)
c RETURN
END[/cpp]
Whew! The end.
Mike
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I need to also pass double precision two - dimensional arrays from the C side to Fortran. Is there anyway you could incorporate this into your example? It believe it should be much easier than passing strings. I have actually done all this using Fortran and Excel, i.e. passing integers, reals, strings (ughh!) and arrays back and forth between Excel (VBA) and Fortran and I know that when passing an array to Fortran from VBA you just pass the first element of the array. I can't figure out how to do this with C though.
That way works only when the array is viewed as a 1-d array on the C side, and not with all compilers,although it was the usual way for f77.So, it's a step backwards from using iso_c_binding with the 1-d C array. Don't try to learn C from us, or, I might venture to say, the way you have been going about it.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That way works only when the array is viewed as a 1-d array on the C side, and not with all compilers,although it was the usual way for f77.So, it's a step backwards from using iso_c_binding with the 1-d C array. Don't try to learn C from us, or, I might venture to say, the way you have been going about it.
tim18,
I tried to post a question once on an MSDN C/C++ related forum dealing whe C/C++ Fortran issues (I don't remember exactly what) and someone over there told me about this very active Intel Fortran group (which I was alreadyvery familiar with)I might try. What am I to do? :)
Mike

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page