Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Shroud to generate the interface automatically #3

Open
ivan-pi opened this issue Sep 28, 2020 · 5 comments
Open

Using Shroud to generate the interface automatically #3

ivan-pi opened this issue Sep 28, 2020 · 5 comments

Comments

@ivan-pi
Copy link
Member

ivan-pi commented Sep 28, 2020

Looking at the C header file in SymEngine, one could probably do everything manually. On the long term however, an automatic tool will probably be easier.

Would using Shroud (https://github.com/LLNL/shroud) be an option?

A presentation (PowerPoint file) is also available from FortranCon: https://tcevents.chem.uzh.ch/event/12/contributions/30/attachments/29/98/Taylor-Shroud-forcon.pptx

@certik
Copy link
Contributor

certik commented Sep 28, 2020

Yes, down the road we should generate it automatically to ensure the interface is consistent with any (future) possible changes to the C API.

@ivan-pi
Copy link
Member Author

ivan-pi commented Sep 29, 2020

Out of curiosity I pulled together the interface routines needed to run the following program:

module small_test

use symengine_cwrapper
use iso_c_binding

implicit none

contains

  function c_char_ptr_to_fstring(c_char_ptr) result(fc)
    type(c_ptr) :: c_char_ptr
    character(len=:,kind=c_char), allocatable :: fc
    character(len=1000,kind=c_char), pointer :: f_string
    
    if (.not. c_associated(c_char_ptr)) then
        fc = ""
    else
        call c_f_pointer(c_char_ptr,f_string)
        fc = f_string(1:index(f_string,c_null_char))
    end if
  end function

subroutine f(i)

  integer(c_long), intent(in) :: i

  type(c_ptr) :: s = c_null_ptr ! string

  type(c_ptr) :: x = c_null_ptr
  type(c_ptr) :: y = c_null_ptr
  type(c_ptr) :: e = c_null_ptr
  type(c_ptr) :: n = c_null_ptr

  type(c_ptr) :: exception = c_null_ptr

  print *, "Symengine version: "//c_char_ptr_to_fstring(symengine_version())

  call basic_new_stack(x)
  call basic_new_stack(y)
  call basic_new_stack(e)
  call basic_new_stack(n)

  exception = symbol_set(x,"x"//c_null_char)
  exception = symbol_set(y,"y"//c_null_char)

  exception = integer_set_si(n, i);
  exception = basic_mul(e, n, x);
  exception = basic_add(e, e, y);

  s = basic_str(e)
  print *, "Result: ", c_char_ptr_to_fstring(s)
  call basic_str_free(s)
  s = c_null_ptr

  print *, c_associated(s), c_char_ptr_to_fstring(s)

  call basic_free_stack(x)
  call basic_free_stack(y)
  call basic_free_stack(e)
  call basic_free_stack(n)

end subroutine

end module

program small_test_program

  use small_test
  use iso_c_binding
  implicit none

  call f(5_c_long)

end program

(Note: the symengine_wrapper module is not included because I have some mistakes left).

After figuring out all the libraries needed to install symengine, I was able to compile the example using:

gfortran -Wall -c symengine_cwrapper.f90
gfortran -Wall -c test_symengine.f90
gfortran -o test_symengine -I/usr/local/include/symengine symengine_cwrapper.o test_symengine.o -L/usr/local/lib -lsymengine -lteuchos -lstdc++ -lmpfr -lgmp -lbfd

And finally, the result of the program:

$ ./test_symengine
 Symengine version: 0.6.0
 Result: 5*x + y
 F

@certik
Copy link
Contributor

certik commented Sep 29, 2020 via email

@ivan-pi
Copy link
Member Author

ivan-pi commented Sep 29, 2020

Could you point me to the files containing the C++ API in the original symengine repository?

Typically, the high-level Fortran API can be made very similar to the C++ one.

@certik
Copy link
Contributor

certik commented Sep 29, 2020

Yes, the main C++ API is in this directory in the header files:

https://github.com/symengine/symengine/tree/master/symengine

For example the Mul class is here:

https://github.com/symengine/symengine/blob/9fc2716cab4a6d2d89a3f9d765a04ef1594c6bcf/symengine/mul.h

The best way to understand how to use it is from tests, e.g., here:

https://github.com/symengine/symengine/blob/9fc2716cab4a6d2d89a3f9d765a04ef1594c6bcf/symengine/tests/basic/test_arit.cpp

This is the main API used throughout SymEngine. Then there is a simpler higher level API here:

https://github.com/symengine/symengine/blob/9fc2716cab4a6d2d89a3f9d765a04ef1594c6bcf/symengine/expression.h
https://github.com/symengine/symengine/blob/9fc2716cab4a6d2d89a3f9d765a04ef1594c6bcf/symengine/tests/expression/test_expression.cpp

Which is just a thin wrapper to provide easier C++ interface, with a possible small performance hit.

For Fortran, I suggest we use the C interface, as it takes care of exceptions and other things, and we build whatever is the most natural in Fortran. If we can somehow overload operators in Fortran and not leak memory, that would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants