1

I am following the following example (http://fortranwiki.org/fortran/show/Fortran+and+Cpp+objects) to adapt a C++ code to Fortran, first I am trying to learn Fortran and making connection between Fortran and C++.

I modified the code where I would like to pass a string from Fortran to C++

#include <iostream>
#include <string>

using namespace std;
class CWLiDAR {
    string name;

public:
    CWLiDAR(string);
};


CWLiDAR::CWLiDAR(string name)
{
    this->name = name;
}


/*
*/

/* C wrapper interfaces to C++ routines */
#ifdef __cplusplus
extern "C" {
#endif
CWLiDAR* CWLiDAR__new(string name)
{
    return new CWLiDAR(name);
}
void CWLiDAR__delete(CWLiDAR* This)
{
    delete This;
}
#ifdef __cplusplus
}
#endif

The Fortran wrapper

module CWLiDAR_module
  use, intrinsic :: ISO_C_Binding!, only: C_int, C_ptr, C_NULL_ptr
  implicit none
  private
  type CWLiDAR_type
    private
    type(C_ptr) :: object = C_NULL_ptr
  end type CWLiDAR_type
  interface
    function C_CWLiDAR__new (name, name_len) result(this) bind(C, name = "CWLiDAR__new")
      import
      type(C_ptr) :: this
      character(name_len, kind=C_CHAR), intent(IN) :: name
      integer :: name_len
    end function C_CWLiDAR__new
    subroutine C_CWLiDAR__delete (this) bind(C, name = "CWLiDAR__delete")
      import
      type(C_ptr), value :: this
    end subroutine C_CWLiDAR__delete
  end interface
  interface new
    module procedure CWLiDAR__new
  end interface new
  interface delete
    module procedure CWLiDAR__delete
  end interface delete
  public :: new, delete, CWLiDAR_type
contains


! Fortran wrapper routines to interface C wrappers
  subroutine CWLiDAR__new(this, name, name_len)
    type(CWLiDAR_type), intent(out) :: this
    character(*) :: name
    integer :: name_len
    this%object = C_CWLiDAR__new(name, name_len)
  end subroutine CWLiDAR__new
  subroutine CWLiDAR__delete(this)
    type(CWLiDAR_type), intent(inout) :: this
    call C_CWLiDAR__delete(this%object)
    this%object = C_NULL_ptr
  end subroutine CWLiDAR__delete
end module CWLiDAR_module

The main

program main
  use cwlidar_module
  type(CWLiDAR_type) :: lidar
  call new(lidar, "Test", 4)

  call delete(lidar)
end program main

How should I modify the Fortran wrapper to pass a string from Fortran to C++?

Mokus
  • 10,174
  • 18
  • 80
  • 122

2 Answers2

1

First of all, you cannot use std::string at the inteface between Fortran and C. std::string is some C++ implementation defined object that has no guaranteed memory layout for interoperability with Fortran.

You must use plain C strings instead. So, CVLiDAR__new() must accept an argument of type char*, and the fortran interface must declare this argument as

character(kind = c_char) :: name(*)

The Fortran wrapper routine must take some Fortran character dummy argument, and proceed to copy its contents into a suitably allocated character(kind = c_char) array, which is then passed to the C function.

At this point, your C interface function may proceed to convert it again into a C++ std::string. This conversion is optional, as C++ can handle C strings just as well as C can. Adding the conversion would allow the rest of the code be more pure C++, though.

Do not forget to correctly terminate the C string with a zero character when you assemble it on the Fortran side!


These argument conversions are indeed a PITA, and if you have any number of interface functions that you need to call from Fortran, it may be advisable to write a generator script that produces the Fortran wrappers. An example of such a script can be found within the Climate Data Interface library (CDI, source code can be found at https://code.mpimet.mpg.de/projects/cdi/files) in the file cdi-1.8.1/interfaces/f2003/bindGen.rb, its output is published in the same tar-ball under cdi-1.8.1/src/mo_cdi.f90. This script may be complete overkill for your case, but it works quite fine for CDI and its output may inspire/help you getting ideas of how to do your conversions right.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • While I agree that C-style strings are necessary for this sort of purpose, that doesn't mean that OP can't use `std::string`. `std::string` is optimized, standard, and generally more friendly in C++ than `char *` and since the `c_str()` function allows you to convert from `std::string` to C-style strings, why not make use of it? (Note: it also [appends a null terminator automatically](https://stackoverflow.com/a/7416473/5209610) to the resulting C-style string). Use the `data()` function if you don't want a null-terminated output. – Vladislav Martin Jun 27 '17 at 13:16
  • @VladislavMartin I see your point, sorry for being too imprecise. I never meant to say that you cannot use `std::string` in your C++ code, I only meant to say that you cannot use it at the Fortran-C interface. I have updated my answer now to be more clear on that. – cmaster - reinstate monica Jun 27 '17 at 13:37
0

You CANNOT pass a C++ string (actually std::string) from Fortran. It is simply not compatible with C or Fortran, it is an internal C++ type. You have to use the C wrapper and pass C compatible character arrays.