/ src / examples / haskell-cxx / ffi.cpp
ffi.cpp
  1  // ffi.cpp
  2  // C++ implementation of FFI functions for Haskell
  3  //
  4  // This demonstrates calling C++ from Haskell via the FFI.
  5  // All exported functions use extern "C" linkage for ABI compatibility.
  6  
  7  #include "ffi.h"
  8  
  9  #include <cmath>
 10  #include <cstring>
 11  #include <numeric>
 12  #include <string>
 13  
 14  // =============================================================================
 15  // Simple arithmetic
 16  // =============================================================================
 17  
 18  extern "C" int32_t ffi_add(int32_t a, int32_t b) { return a + b; }
 19  
 20  extern "C" int32_t ffi_multiply(int32_t a, int32_t b) { return a * b; }
 21  
 22  // =============================================================================
 23  // Vector operations
 24  // =============================================================================
 25  
 26  extern "C" double ffi_dot_product(const double *a, const double *b,
 27                                    size_t len) {
 28    double result = 0.0;
 29    for (size_t i = 0; i < len; ++i) {
 30      result += a[i] * b[i];
 31    }
 32    return result;
 33  }
 34  
 35  extern "C" double ffi_norm(const double *v, size_t len) {
 36    return std::sqrt(ffi_dot_product(v, v, len));
 37  }
 38  
 39  extern "C" void ffi_scale(double *v, size_t len, double scalar) {
 40    for (size_t i = 0; i < len; ++i) {
 41      v[i] *= scalar;
 42    }
 43  }
 44  
 45  // =============================================================================
 46  // String operations
 47  // =============================================================================
 48  
 49  extern "C" char *ffi_greet(const char *name) {
 50    std::string greeting = "Hello from C++, " + std::string(name) + "!";
 51  
 52    // Allocate with malloc so Haskell can free with ffi_free_string
 53    char *result = static_cast<char *>(std::malloc(greeting.size() + 1));
 54    if (result) {
 55      std::strcpy(result, greeting.c_str());
 56    }
 57    return result;
 58  }
 59  
 60  extern "C" void ffi_free_string(char *str) { std::free(str); }
 61  
 62  // =============================================================================
 63  // Counter (opaque handle pattern)
 64  // =============================================================================
 65  
 66  // C++ class behind the opaque handle
 67  class CounterImpl {
 68  public:
 69    explicit CounterImpl(int32_t initial) : value_(initial) {}
 70  
 71    int32_t get() const { return value_; }
 72    int32_t increment() { return ++value_; }
 73    int32_t add(int32_t n) {
 74      value_ += n;
 75      return value_;
 76    }
 77  
 78  private:
 79    int32_t value_;
 80  };
 81  
 82  // The Counter struct is just an alias for the C++ class
 83  struct Counter : public CounterImpl {
 84    using CounterImpl::CounterImpl;
 85  };
 86  
 87  extern "C" Counter *ffi_counter_new(int32_t initial) {
 88    return new Counter(initial); // NOLINT(aleph-cpp-raw-new-delete)
 89  }
 90  
 91  // NOLINTNEXTLINE(aleph-cpp-raw-new-delete)
 92  extern "C" void ffi_counter_free(Counter *counter) { delete counter; }
 93  
 94  extern "C" int32_t ffi_counter_get(const Counter *counter) {
 95    return counter->get();
 96  }
 97  
 98  extern "C" int32_t ffi_counter_increment(Counter *counter) {
 99    return counter->increment();
100  }
101  
102  extern "C" int32_t ffi_counter_add(Counter *counter, int32_t n) {
103    return counter->add(n);
104  }