Question

Good day. Sorry for maybe not so understandable definition of my problem and maybe some inaccuracies - I'm just starting to try myself in programming. Still, I'll try my best to explain everything plain.

I have mathematical DLL written in Fortran.

For example, there is a function. This function is used to parse name of log file into the dll to watch for the calculations.

  integer function initLog(
 *                          int_parameter,
 *                          char_parameter,
 *                          char_parameter_length,
 *                        )
 *bind(C, name = "initLog");
  use, intrinsic :: ISO_C_BINDING;
  !DEC$ATTRIBUTES DLLEXPORT::initLog

    integer(C_INT),         value :: parameter;
    character(C_CHAR), intent(in) :: char_parameter(char_parameter_length);
    integer(C_INT),         value :: char_parameter_length;

    ...

    some_other_variable = char_parameter(1:1)(1:char_parameter_length); 

  end function;

Usually I use MATLAB to work with the dll and, thus, have to use .mex files to call my functions directly from MATLAB. Inside the .mex file I have some interface code written in C that provides the interface between MATLAB and dll. For example, C interface for the function mentioned is:

int doSmth(const int   int_parameter,
           const char* char_parameter,
           const int   char_parameter_length,);

And then I use loadLibrary and GetProcAddress to get the function. And this works fine.

However, now I need to create .exe test file in Fortran which would use my dll. So, I have to link my dll to exe by linking it to an import .lib library. Another option for this executable is to take the name of the log file via command line as parameter. So, first I tried to pass the logfile filename just from within the exe file, like this:

program test
  use dll_name;
  use ifport;
  implicit none;
  ...
  integer :: log_init_status;
  ...
  log_init_status = init_log(2, 'logfile.log', len('logfile.log'));
  ...
end program

This works fine in release, but returns a "severe (664): Out of range: substring ending position '11' is greater than string length '1'" mistake in debug. But at first I didn't find this bug and kept on writing the code. This is what I've got now:

program test
  use dll_name;
  use ifport;
  use ISO_C_BINDING;
  implicit none;
  ...
  character*255     :: log_flag_char;
  integer(C_INT)    :: log_flag;
  character*255     :: filename;
  character(C_CHAR) :: log_filename;
  integer(C_INT)    :: log_filename_length;

  ....

  call getarg(5, log_flag_char);
  read(log_flag_char, *) log_flag;

  call getarg(6, log_filename);
  log_filename_length = len(log_filename);

  log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);

  ...

end program

This worked fine, but took only 1 first character of the log_filename ("C:\abcd\logfile.log" is transformed into "C"). If I change

character(C_CHAR) :: log_filename;

to

character(C_CHAR) :: log_filename(255);

, I get 2 problems: first, I have the length of my log_filename equal to 255 (can be fixed by trim though), and second - and the main - I again get "severe (664): Out of range: substring ending position '255' is greater than string length '1'".

If I change

log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);

to

log_init_status = analyticsLogInit(log_flag, C_LOC(log_filename), log_filename_length);

, I get the error about the dummy argument type differ than the actual one.

I myself have a feeling that the 664 error shown comes from this line in dll:

some_other_variable = char_parameter(1:1)(1:char_parameter_length);

. I should write in my exe something like

character*255 :: log_filename;

and not

character :: log_filename(255);

But how can I parse it with (C_CHAR) used?

I realise that all this is quite messy and that it all comes from the leak of understanding, but this is my almost first serious experience in programming.

Was it helpful?

Solution

I only glanced over your question, but one thing to take note of is the way a character variable or named constant is declared. You can provide two type parameters: length and kind. If you don't use the corresponding keyword in the declaration, the first parameter specifies the length, and the second (if present) specifies the kind.
So if you want to declare a character variable of length 255 and kind C_CHAR, you can do so in any of the following ways:

character(len=255, kind=C_CHAR) :: log_filename
character(255, kind=C_CHAR)     :: log_filename
character(255, C_CHAR)          :: log_filename
character(kind=C_CHAR, len=255) :: log_filename
character(kind=C_CHAR)          :: log_filename*255

The following syntax on the other hand (which is the one you used), declares a character variable of length C_CHAR, whatever value that may be.

character(C_CHAR) :: log_filename

Oh, and the next syntax declares an array of 255 elements, each element being a character variable of length C_CHAR.

character(C_CHAR) :: log_filename(255)

So the conclusion is, that one should take some time to study the peculiarities of declaring character entities in fortran.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top