/ 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) {
 19    return a + b;
 20  }
 21  
 22  extern "C" int32_t ffi_multiply(int32_t a, int32_t b) {
 23    return a * b;
 24  }
 25  
 26  // =============================================================================
 27  // Vector operations
 28  // =============================================================================
 29  
 30  extern "C" double ffi_dot_product(const double* a, const double* b, size_t len) {
 31    double result = 0.0;
 32    for (size_t i = 0; i < len; ++i) {
 33      result += a[i] * b[i];
 34    }
 35    return result;
 36  }
 37  
 38  extern "C" double ffi_norm(const double* v, size_t len) {
 39    return std::sqrt(ffi_dot_product(v, v, len));
 40  }
 41  
 42  extern "C" void ffi_scale(double* v, size_t len, double scalar) {
 43    for (size_t i = 0; i < len; ++i) {
 44      v[i] *= scalar;
 45    }
 46  }
 47  
 48  // =============================================================================
 49  // String operations
 50  // =============================================================================
 51  
 52  extern "C" char* ffi_greet(const char* name) {
 53    std::string greeting = "Hello from C++, " + std::string(name) + "!";
 54  
 55    // Allocate with malloc so Haskell can free with ffi_free_string
 56    char* result = static_cast<char*>(std::malloc(greeting.size() + 1));
 57    if (result) {
 58      std::strcpy(result, greeting.c_str());
 59    }
 60    return result;
 61  }
 62  
 63  extern "C" void ffi_free_string(char* str) {
 64    std::free(str);
 65  }
 66  
 67  // =============================================================================
 68  // Counter (opaque handle pattern)
 69  // =============================================================================
 70  
 71  // C++ class behind the opaque handle
 72  class CounterImpl {
 73  public:
 74    explicit CounterImpl(int32_t initial) : value_(initial) {}
 75  
 76    int32_t get() const { return value_; }
 77    int32_t increment() { return ++value_; }
 78    int32_t add(int32_t n) {
 79      value_ += n;
 80      return value_;
 81    }
 82  
 83  private:
 84    int32_t value_;
 85  };
 86  
 87  // The Counter struct is just an alias for the C++ class
 88  struct Counter : public CounterImpl {
 89    using CounterImpl::CounterImpl;
 90  };
 91  
 92  extern "C" Counter* ffi_counter_new(int32_t initial) {
 93    return new Counter(initial); // NOLINT(aleph-cpp-raw-new-delete)
 94  }
 95  
 96  // NOLINTNEXTLINE(aleph-cpp-raw-new-delete)
 97  extern "C" void ffi_counter_free(Counter* counter) {
 98    delete counter;
 99  }
100  
101  extern "C" int32_t ffi_counter_get(const Counter* counter) {
102    return counter->get();
103  }
104  
105  extern "C" int32_t ffi_counter_increment(Counter* counter) {
106    return counter->increment();
107  }
108  
109  extern "C" int32_t ffi_counter_add(Counter* counter, int32_t n) {
110    return counter->add(n);
111  }