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.