If you use C++17 (or higher), you can have high level code just like in Fortran.
#include <array>
#include <numeric>
#include "Fcpp.h" // <-- C++ descriptor adaptor
// Fortran routine
extern "C" void sub(CFI_cdesc_t *);
int main(void)
{
using namespace Fcpp;
std::array<int,5> x;
std::iota(x.begin(),x.end(),42); // or std::ranges::iota(x,42) in C++23
sub(desc(x)); /* Create adaptor on the fly */
return 0;
}
~/tmp$ gfortran-13 -c m.f90 # module providing routine sub
~/tmp$ g++-13 -Wall -std=c++17 wrapper.cpp m.o -lgfortran
~/tmp$ ./a.out
In Fortran sub: size(x) = 5
42 43 44 45 46
How does it work? Well you create an adaptor using a templated class; all the ugly work is hidden in the constructor. Click on the little arrow (triangle) for a look at the internals:
Fcpp - Adaptor between C++ and Fortran
#include <cassert>
#include <array>
#include <vector>
#include "ISO_Fortran_binding.h"
namespace Fcpp {
namespace {
// Type converters
template<typename T>
constexpr CFI_type_t type(){ return CFI_type_other; };
template<>
constexpr CFI_type_t type<float>(){ return CFI_type_float; }
template<>
constexpr CFI_type_t type<double>(){ return CFI_type_double; }
template<>
constexpr CFI_type_t type<int>(){ return CFI_type_int; }
}
// Descriptor Adaptor - provides Fortran descriptor of a C++ entity
template<typename T>
class desc {
public:
desc(T* ptr, int n0) {
CFI_index_t extents[] = {
static_cast<CFI_index_t>(n0)};
[[maybe_unused]] int status = CFI_establish(
this->get(),
ptr,
CFI_attribute_other,
type<T>(),
sizeof(T),
1,
extents
);
assert(status == CFI_SUCCESS);
}
// Constructor from std::vector
desc(std::vector<T> &buffer) : desc(buffer.data(),buffer.size()) {}
// Constructor from std::array
template<std::size_t N>
desc(std::array<T,N> &buffer) : desc(buffer.data(),N) {}
// Return pointer to the underlying descriptor
constexpr auto get() const {
return (CFI_cdesc_t *) &desc_;
}
// Implicit cast to C-descriptor pointer
operator CFI_cdesc_t* () { return this->get(); }
private:
CFI_CDESC_T(1) desc_;
};
} // namespace Fcpp