
Automated Fortran–C++ Bindings for Scientific Applications Seth R Johnson High Performance Computing Methods & Applications Team Reactor and Nuclear Systems Division Oak Ridge National Laboratory ORNL is managed by UT–Battelle, LLC for the US Department of Energy Project collaborators: Matt Bement, Kate Evans, Tim Fuller, Andrey Prokopenko github.com/swig-fortran Motivation • C++ library developers: expand user base, more opportunities for development and follow-on funding • Fortran scientific app developers: use newly exposed algorithms and tools for your code • Multiphysics project integration: in-memory coupling of C++ physics code to Fortran physics code • Transitioning application teams: bite-size migration from Fortran to C++ !2 Background • Exascale Computing Project: many petascale scientific app codes still are primarily Fortran • Numerical/scientific libraries are primarily C/C++ • Expose Trilinos solver library to Fortran app developers: ForTrilinos product !3 Supported SWIG: Simplified Wrapper and Interface Generator Languages • Allegro CL • C# • CFFI • CLISP • Chicken • Generate interfaces to existing C and C++ code and data • D • Go types so that a target language can invoke functions and • Guile use the data • Java • Javascript • “Glue” code: flat C-linkage wrappers to C++ functions, • Lua • Modula-3 corresponding interfaces in target language • Mzscheme • OCAML • Does not couple target languages to other target languages • Octave • Perl • PHP • Does not parse target languages or create C++ proxy • Python wrappers • R • Ruby • Scilab • Tcl • UFFI !4 SWIG execution sequence • SWIG reads .i interface file which may %include other C/C++ headers to parse them • Interface file can be as little as a couple of lines for simple interfaces • More difficult C++/Fortran conversions require more interface code • SWIG generates source code files: .cxx and .f90 • This wrapper code can be distributed like regular source files • Library users don’t need to know SWIG User-written code SWIG-generated code Foo.cxx Foo.hh SWIG foo.f90 main.f90 Foo.i foo_wrap.cxx !5 Control flow and data conversion Call “native” procedure user.f90 Public module procedure Convert Fortran types to ISO_C_BINDING types module.f90 Call BIND(C) interface • Fortran 2003 standard defines C-compatible datatypes • Use only Fortran-compatible ISO C datatypes extern "C" wrapper function Convert C type to C++ type • Minimize data movement of numerical arrays Call C++ library function module_wrap.cxx C++ Library code library.cxx !6 Features • Primitive types • Function overloading • Enumerations • Template instantiation • Classes (with inheritance) • Compile-time constants • C strings and std::string • Exception handling • Function pointers • OpenACC and Thrust • Arrays and std::vector !7 Simple addition function: input and generated C code F/C interface #ifndef simplest_h SWIGEXPORT int _wrap_add( #define simplest_h int const *farg1, int add(int a, int b); #endif int const *farg2) { int fresult ; Input argument int arg1 ; conversion simplest.h int arg2 ; int result; arg1 = (int)(*farg1); arg2 = (int)(*farg2); %module simplest Wrapped result = (int)add(arg1,arg2); function call %include "simplest.h" fresult = (int)(result); return fresult; } simplest.i Output simplest_wrap.c argument conversion !8 Simple addition function: generated Fortran Fortran proxy function module simplest F/C .... use, intrinsic :: ISO_C_BINDING implicit none interface function add(a, b) & private result(swig_result) public :: add integer(C_INT) :: swig_result interface integer(C_INT), intent(in) :: a function swigc_add(farg1, farg2) & integer(C_INT), intent(in) :: b bind(C, name="_wrap_add") & integer(C_INT) :: fresult Input result(fresult) integer(C_INT) :: farg1 argument integer(C_INT), intent(in) :: farg1 integer(C_INT) :: farg2 integer(C_INT), intent(in) :: farg2 conversion integer(C_INT) :: fresult farg1 = a Wrapper end function farg2 = b function call end interface fresult = swigc_add(farg1, farg2) contains Output swig_result = fresult argument end function .... conversion end module simplest.f90 !9 Simple addition function: the part app devs care about SWIGEXPORT int swigc_add(int const *farg1, program main int const *farg2); use simplest, only : add write (0,*) add(10,20) end program simplest_wrap.c module simplest use, intrinsic :: ISO_C_BINDING main.f90 … contains function add(a, b) & result(swigf_result) integer(C_INT) :: swigf_result integer(C_INT), intent(in) :: a integer(C_INT), intent(in) :: b … end function $ ./main.exe end module simplest.f90 30 !10 Templated class Insert raw C++ template<typename T> %module "templated" Tell SWIG to class Thing { code into parse the T val_; generated %{ header file public: wrapper file #include "templated.hpp" Thing(T val); %} T get() const; %include "templated.hpp" }; // Instantiate templated classes template<typename T> %template(Thing_Int) Thing<int>; void print_thing(const Thing<T>& t); %template(Thing_Dbl) Thing<double>; // Instantiate and overload a function Instantiate %template(print_thing) print_thing<int>; templates %template(print_thing) print_thing<double>; at SWIG time templated.i !11 Templated class: generated Fortran wrapper code Memory module templated ownership interface print_thing … module procedure swigf_print_thing__SWIG_1, integer, parameter, public :: swig_cmem_own_bit = 0 swigf_print_thing__SWIG_2 integer, parameter, public :: swig_cmem_rvalue_bit = 1 end interface integer, parameter, public :: swig_cmem_const_bit = 2 public :: print_thing type, bind(C) :: SwigClassWrapper interface Overloaded type(C_PTR), public :: cptr = C_NULL_PTR subroutine swigc_delete_Thing_Int(farg1) & integer(C_INT), public :: cmemflags = 0 bind(C, name="_wrap_delete_Thing_Int") function end type ! class Thing< int > Opaque class … type, public :: Thing_Int wrapper contains type(SwigClassWrapper), public :: swigdata subroutine swigf_release_Thing_Int(self) Call delete if contains use, intrinsic :: ISO_C_BINDING we “own” procedure :: get => swigf_Thing_Int_get class(Thing_Int), intent(inout) :: self procedure :: release => swigf_release_Thing_Int type(SwigClassWrapper) :: farg1 the memory procedure, private :: swigf_Thing_Int_op_assign__ farg1 = self%swigdata generic :: assignment(=) => swigf_Thing_Int_op_assign__ end type Thing_Int if (btest(farg1%cmemflags, swig_cmem_own_bit)) then interface Thing_Int call swigc_delete_Thing_Int(farg1) module procedure swigf_create_Thing_Int Fortran endif end interface proxy class farg1%cptr = C_NULL_PTR ! class Thing< double > Second farg1%cmemflags = 0 type, public :: Thing_Dbl self%swigdata = farg1 … template end subroutine end type Thing_Dbl … instantiation … end module templated.f90 (2/2) !12 Exception handling %module except program main %include <std_except.i> use except, only : do_it, do_it_again, ierr, get_serr %exception { call do_it(-3) if (ierr /= 0) then SWIG_check_unhandled_exception(); write(0,*) "Got error ", ierr, ": ", get_serr() try { Replaced with ierr = 0 $action endif } wrapper call call do_it(2) catch (const std::exception& e) { call do_it(-2) SWIG_exception(SWIG_RuntimeError, e.what()); call do_it_again(321) } end program } main.f90 %{ #include <stdexcept> Got error -3 : In do_it(int): #include <iostream> %} NOOOOO %inline { Yes! I got 2 void do_it(int i) { terminate called after throwing an if (i < 0) throw std::logic_error("NOOOOO"); instance of 'std::runtime_error' std::cout << "Yes! I got " << i what(): An unhandled exception << std::endl; occurred before a call to } void do_it_again(int i) { do_it(i); } do_it_again(int); in do_it(int): NOOOOO } !13 Array views subroutine sort(data) use, intrinsic :: ISO_C_BINDING integer(C_INT), dimension(:), target :: data integer(C_INT), pointer :: farg1_view %module algorithm type(SwigArrayWrapper) :: farg1 Treat as native if (size(data) > 0) then %{ Fortran array farg1_view => data(1) #include <algorithm> farg1%data = c_loc(farg1_view) %} farg1%size = size(data) else %include <typemaps.i> farg1%data = c_null_ptr %apply (SWIGTYPE *DATA, size_t SIZE) { farg1%size = 0 (int* data, std::size_t size) }; end if call swigc_sort(farg1) %inline { end subroutine void sort(int* data, std::size_t size) { program main std::sort(data, data + size); use, intrinsic :: ISO_C_BINDING } use algorithm, only : sort } implicit none integer(C_INT), dimension(6) & :: test_int = (/ 3, -1, 7, 3, 1, 5 /) call sort(test_int) write (*,*) test_int end program !14 Thrust + OpenACC %module thrustacc; program test_thrustacc use thrustacc %include <openacc.i> implicit none %include <thrust.i> integer, parameter :: n = 32768 integer :: i real, dimension(:), allocatable :: a %{ #include <thrust/sort.h> ! Generate N uniform numbers on [0,1) %} allocate(a(n)) %inline %{ call random_number(a) template<typename T> void swig_thrust_sort(thrust::device_ptr<T> !$acc data copy(a) DATA, size_t SIZE) { !$acc kernels do i = 1,n thrust::sort(DATA, DATA + SIZE); a(i) = a(i) * 10 + i } end do %} !$acc end kernels call sort(a) %template(sort) swig_thrust_sort<int>; !$acc end data %template(sort) swig_thrust_sort<float>; write(*,*) sum(a) %template(sort) swig_thrust_sort<double>; end program !15 Automatic BIND(C) wrapping %module bindc module bindc Only generate … interface code type, bind(C), public :: Point %fortranbindc; real(C_FLOAT), public :: x real(C_FLOAT), public :: y %fortran_struct(Point) Don’t create real(C_FLOAT), public :: z end type Point proxy class … %inline { interface typedef struct { float x, y, z; } Point; subroutine print_point(p) & bind(C, name="print_point")
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages20 Page
-
File Size-