swig-fortran / swig

This fork of SWIG creates Fortran wrapper code from C++ headers.
http://www.swig.org
Other
44 stars 11 forks source link

Problems when wrapping The C++ mesh generator Tetgen #165

Closed luogyong closed 2 years ago

luogyong commented 3 years ago

fortran_tetgen.zip

Hi, I tried to call tetgen from fortran but I failed. I passed the compilation and got the exe file. When I run the exe, I got the error message as follows:

*terminate called after throwing an instance of 'std::logic_error' what(): In tetgenio::initialize(): Cannot pass null tetgenio (class tetgenio) as a reference

Program received signal SIGABRT: Process abort signal.**

The file used was attached below. And the directives used are swig -c++ -fortran tetgen.i gcc -c tetgen.cxx -o tetgencxx.o -D "TETLIBRARY" gcc -c tetgen_wrap.cxx predicates.cxx gfortran -c tetgen.f90 ar cr tet.a lorder tetgen.o tetgencxx.o tetgen_wrap.o predicates.o | tsort gfortran -g main_tetgen.f90 tet.a -lstdc++ -o ftetgen.exe

In addition, since SWIG does not seem to support nesting of classes, in the interface file, I copy the nested class to the outside so that it can be compiled successfully. I don't know if this is the source of my problem.

Can any one help me out? Thank you.

sethrj commented 3 years ago

Hi @luogyong, sorry for not seeing your post before now! The problem you're having is that your in object is not constructed when you call %initialize. To allocate an object wrapped by SWIG-Fortran, you must construct it:

  type(tetgenio):: in
  in = tetgenio()

Since your constructor calls initialize as a side effect, you do not need to call in%initialize after that.

To use nested classes natively, you can use the flatnested feature of SWIG:

%warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) Class::NestedClass;
%feature("flatnested") Class::NestedClass;
%rename(MyNestedClass) Class::NestedClass;
luogyong commented 3 years ago

Hi @luogyong, sorry for not seeing your post before now! The problem you're having is that your in object is not constructed when you call %initialize. To allocate an object wrapped by SWIG-Fortran, you must construct it:

  type(tetgenio):: in
  in = tetgenio()

Since your constructor calls initialize as a side effect, you do not need to call in%initialize after that.

To use nested classes natively, you can use the flatnested feature of SWIG:

%warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) Class::NestedClass;
%feature("flatnested") Class::NestedClass;
%rename(MyNestedClass) Class::NestedClass;

It works. Thank you very much. @sethrj

luogyong commented 3 years ago

Hi, @sethrj , I run into another problem. I don't know how to prepare data passed to C++ from Fortran. The Swig-Fortran seems hide many variables in swigdata class. For example, a class tetgenio,

class tetgenio {
            public:
              typedef struct {
                int *vertexlist;
                int numberofvertices;
              } polygon;
              typedef struct {
                polygon *polygonlist;
                int numberofpolygons;
                REAL *holelist;
                int numberofholes;
              } facet;

              int numberoffacets;
              facet *facetlist;
            }; // class tetgenio

was translate into a fortran class as something like this,

//......
         type, bind(C) :: SwigClassWrapper
          type(C_PTR), public :: cptr = C_NULL_PTR
          integer(C_INT), public :: cmemflags = 0
         end type
         type, public :: SWIGTYPE_p_tetgenio__facet
          type(SwigClassWrapper), public :: swigdata
         end type

         public :: SWIGTYPE_f_p_void_int_int__double
         public :: SWIGTYPE_f_p_void_int_double_p_double__void
         public :: SWIGTYPE_f_p_void_int_int_p_double__void
         public :: SWIGTYPE_f_p_void_int_double_int_p_double__void
         public :: SWIGTYPE_f_p_void_int_p_double_p_double__void
         public :: SWIGTYPE_f_p_double_p_double_p_double_p_double_p_double_doE5JNE
         type, bind(C) :: SwigArrayWrapper
          type(C_PTR), public :: data = C_NULL_PTR
          integer(C_SIZE_T), public :: size = 0
         end type
         type, public :: SWIGTYPE_p_tetgenio__polygon
          type(SwigClassWrapper), public :: swigdata
         end type

        type, public :: tetgenio
          type(SwigClassWrapper), public :: swigdata
         contains

          procedure :: set_facetlist => swigf_tetgenio_facetlist_set
          procedure :: get_facetlist => swigf_tetgenio_facetlist_get

            //......

         end type tetgenio

         subroutine swigf_tetgenio_facetlist_set(self, facetlist)
        use, intrinsic :: ISO_C_BINDING
        class(tetgenio), intent(in) :: self
        class(SWIGTYPE_p_tetgenio__facet), intent(in) :: facetlist
        type(SwigClassWrapper) :: farg1 
        type(SwigClassWrapper) :: farg2 

        farg1 = self%swigdata
        farg2 = facetlist%swigdata
        call swigc_tetgenio_facetlist_set(farg1, farg2)
        end subroutine

        function swigf_tetgenio_facetlist_get(self) &
        result(swig_result)
        use, intrinsic :: ISO_C_BINDING
        type(SWIGTYPE_p_tetgenio__facet) :: swig_result
        class(tetgenio), intent(in) :: self
        type(SwigClassWrapper) :: fresult 
        type(SwigClassWrapper) :: farg1 

        farg1 = self%swigdata
        fresult = swigc_tetgenio_facetlist_get(farg1)
        swig_result%swigdata = fresult
        end function

        //...

My problem is I don't how to prepare a facelist array data passed to subroutine swigc_tetgenio_facetlist_set. That is, I don't know how to tranlate the following C++ code into SWIGTYPE_p_tetgenio__facet type ffacelist.

    //......

    tetgenio in, out;
    tetgenio::facet *f;
    tetgenio::polygon *p;

    in.numberoffacets = 6;
    in.facetlist = new tetgenio::facet[in.numberoffacets];
    in.facetmarkerlist = new int[in.numberoffacets];

    // Facet 1. The leftmost facet.
    f = &in.facetlist[0];
    f->numberofpolygons = 1;
    f->polygonlist = new tetgenio::polygon[f->numberofpolygons];
    f->numberofholes = 0;
    f->holelist = NULL;
    p = &f->polygonlist[0];
    p->numberofvertices = 4;
    p->vertexlist = new int[p->numberofvertices];
    p->vertexlist[0] = 1;
    p->vertexlist[1] = 2;
    p->vertexlist[2] = 3;
    p->vertexlist[3] = 4;

    //...

So that, I can call the set subroutine

    `call in.swigf_tetgenio_facetlist_set(ffacelist)`

Can you give me an example how to do it?

Thank you.

sethrj commented 3 years ago

Yes, C arrays can be a little awkward with SWIG. You should be able to use the carrays feature to do this. SWIG-Fortran is targeted for two main use cases: low-level C-compatible types, and high-level object-oriented C++ types. The nested classes with manual memory management and pointer assignment in your class interface don't exactly match either category so will be a little harder to get working.