Enhancements to allocatable arrays

In C++ std::vector is a class template, with the internals looking something like this:

template <class T, class A = std::allocator<T> >
class vector {
public:
    // public member functions
private:
    T*                    data_;
    typename A::size_type capacity_;
    typename A::size_type size_;
    A                     allocator_;
};

I’ve been contemplating implementing a Fortran vector, using the F2018 enhanced interoperability, which would look something like this:

#include <ISO_Fortran_binding.h>

namespace Fcpp {

template <typename T, bool owning>
class vector {
public:
    // public member functions
private:
    CFI_cdesc_t *ptr;           // CFI_attribute_allocatable
    CFI_index_t capacity_;
    CFI_index_t size_;
};

}

Essentially, it would have the same behavior and methods as the C++ std::vector, but it would use the Fortran runtime under the hood. Depending on the template parameter owning it could either own the memory, just like the C++ containers (excluding span) are supposed to, or it could be used as a “view” of an existing Fortran-allocated array.

In case non-owning, the vector would only be constructable from an existing C descriptor:

// Create "vector" view of a Fortran allocatable array
vector(CFI_cdesc_t *x)

In case owning, the vector would have a method to “release” the allocatable array out before destruction, similar to the release method of std::unique_ptr. I haven’t thought this part through completely.

Just to give an idea, it would look something like this:

use, intrinsic :: iso_c_binding, only: dp => c_double
implicit none
interface
  subroutine extend(a,b) bind(c)
     import dp
     real(dp), intent(inout), allocatable :: a(:)
     real(dp), intent(in) :: b(:)
  end subroutine
end interface

real(dp), allocatable :: a(:), b(:)

! ... initialize a and b
a = [1, 2, 3]
b = [4, 5]

! In Fortran, extension is trivial
a = [a,b]

! Now in C++ using F2018
call extend(a,b)

print *, a  ! [1., 2., 3., 4., 5., 4., 5.]
end
// extend.cpp

#include <Fcpp.h>

using namespace Fcpp;

extern "C" 
void extend(CFI_cdesc_t *a_, CFI_cdesc_t *b_)  {

   vector<double,false> a(a_);      // A Fortran "resizable" array
   cdesc_ptr<const double,1> b(b_); // Just a plain array

   // Using iterators
   a.insert(a.end(),b.begin(),b.end());

   // Or range-based for
   // for (auto bb : b) a.push_back(bb);

   a.shrink_to_fit();
}

Maybe the fact one can (probably) do this in C++ already could help sharpen your proposal?

I had the idea while preparing my poster for PASC but didn’t find time to prototype it yet. The poster was accepted well and won an award. The whole story is in this interview.

2 Likes

@ivanpribec I can see the general idea but I’m not really skilled in C++ to fully understand what you are doing. Are you redefining a vector class?

13 posts were split to a new topic: OpenMP and Allocate

Yes, however I’m not redefining the existing one, but a new one in the Fcpp namespace. Instead of new and delete (C++ memory allocation functions), the Fcpp vector would use CFI_allocate and CFI_deallocate. The class constructor calls CFI_establish.

The usage semantics would remain similar to std::vector, just internally it would be using memory allocated through the Fortran runtime library.

I have written some (limited) demonstration code to show how it could work. The code is non-standard and relies on the C interoperability to manipulate the array descriptors in C.

For instance, the element-by-element appending operation

integer, allocatable :: a(:)
allocate( a(0) )
do i = 1, n
   k = <whatever>
   a = [a, k]
end do

is dramatically faster with

integer, allocatable :: a(:)
do i = 1, n
   k = <whatever>
   call resize(a,extend=k)
end do

This would be quite easy to implement in the compilers, I think…

1 Like