packages feed

hasquant-0.8.0.0: cbits/qlCallback.cpp

#include <ql/version.hpp>
#include "qlaux.h"

namespace {
  thread_local QlCallScope* currentCall = nullptr;
  class CallbackFailure : public std::exception {
  public:
    const char* what() const noexcept override { return "Haskell callback failed"; }
  };
}

QlCallScope::QlCallScope(QlError** error) noexcept : slot(error), previous_(currentCall) {
  currentCall = this;
}
QlCallScope::~QlCallScope() {
  currentCall = previous_;
  // Only the outermost scope collects deferred objects; no interrupted call can use them now.
  for (auto& [object, destroy] : deferred_) destroy(object);
}

void QlCallScope::deleteAfterCall(void* object, void (*destroy)(void*)) noexcept {
  QlCallScope* outermost = currentCall;
  while (outermost && outermost->previous_) outermost = outermost->previous_;
  if (outermost) {
    try { outermost->deferred_.emplace_back(object, destroy); return; } catch (...) {}
  }
  destroy(object);
}

void qlSetError(QlError** slot, const char* message) {
  if (!*slot) *slot = ret(new QlError(message));
}

void qlUnsupportedVersion(QlError** slot, const char* call, const char* required) {
  if (*slot) return;
  auto error = std::make_unique<QlError>(std::string(call) + " requires QuantLib " + required);
  error->call = call;
  error->requiredVersion = required;
  error->linkedVersion = QL_VERSION;
  *slot = ret(error.release());
}

bool ql144Default(QlError** slot, const char* call, bool isDefault) {
#if QL_HEX_VERSION >= 0x01440000
  (void)slot; (void)call; (void)isDefault;
  return true;
#else
  if (isDefault) return true;
  qlUnsupportedVersion(slot, call, "1.44");
  return false;
#endif
}

void hasquant::Callback::invoke(const QlCallbackArgs& args) const {
  if (currentCall && *currentCall->slot) throw CallbackFailure();
  void* failure = fn_(&args);
  if (!failure) return;
  std::unique_ptr<void, QlReleaseStable> pending(failure, releaseStable_);
  if (currentCall) {
    qlSetError(currentCall->slot, "Haskell callback failed");
    (*currentCall->slot)->releaseException = releaseStable_;
    (*currentCall->slot)->exception = pending.release();
  }
  // Haskell has returned; only the native exception unwinds native frames.
  throw CallbackFailure();
}

QlCallback* qlNewCallback(QlCallbackFun fn, QlReleaseFun releaseFun,
                          QlReleaseStable releaseStable, QlError** e) {
  QlCallScope callbackScope(e);
  try {
    auto callback = std::shared_ptr<hasquant::Callback>(alloc(new hasquant::Callback(fn, releaseStable)));
    auto result = std::make_unique<QlCallback>(std::move(callback));
    // Haskell still owns the function pointer if either allocation above fails.
    (*result)->adopt(releaseFun);
    return ret(result.release());
  } catch (std::exception& error) { return handleException<QlCallback*>(e, error); }
}
void qlFreeCallback(QlCallback* callback) { del(callback); }
const char* qlErrorMessage(const QlError* error) { return error->message.c_str(); }
const char* qlErrorCall(const QlError* error) { return error->call.c_str(); }
const char* qlErrorRequiredVersion(const QlError* error) { return error->requiredVersion.c_str(); }
const char* qlErrorLinkedVersion(const QlError* error) { return error->linkedVersion.c_str(); }
void* qlTakeErrorException(QlError* error) { return std::exchange(error->exception, nullptr); }
void qlFreeError(QlError* error) { del(error); }

int qlSupports144(void) {return QL_HEX_VERSION >= 0x01440000;}