#pragma once
#include <wren.hpp>
#include <iostream>
#include <sstream>
#include <ostream>
#include <unordered_map>
#include "allocator.hpp"
#include "caller.hpp"
namespace wrenbind17 {
enum ForeignMethodOperator {
OPERATOR_GET_INDEX,
OPERATOR_SET_INDEX,
OPERATOR_SUB,
OPERATOR_ADD,
OPERATOR_MUL,
OPERATOR_DIV,
OPERATOR_NEG,
OPERATOR_MOD,
OPERATOR_EQUAL,
OPERATOR_NOT_EQUAL,
OPERATOR_GT,
OPERATOR_LT,
OPERATOR_GT_EQUAL,
OPERATOR_LT_EQUAL,
OPERATOR_SHIFT_LEFT,
OPERATOR_SHIFT_RIGHT,
OPERATOR_AND,
OPERATOR_XOR,
OPERATOR_OR
};
class ForeignMethod {
public:
ForeignMethod(std::string name, WrenForeignMethodFn method, const bool isStatic)
: name(std::move(name)), method(method), isStatic(isStatic) {
}
virtual ~ForeignMethod() = default;
// If you are getting "referencing deleted function" error which is this one below,
// then you are trying to make a copy of ForeignKlass during vm.klass<Foo>("Foo");
ForeignMethod(const ForeignMethod& other) = delete;
virtual void generate(std::ostream& os) const = 0;
const std::string& getName() const {
return name;
}
WrenForeignMethodFn getMethod() const {
return method;
}
bool getStatic() const {
return isStatic;
}
protected:
std::string name;
WrenForeignMethodFn method;
bool isStatic;
};
class ForeignProp {
public:
ForeignProp(std::string name, WrenForeignMethodFn getter, WrenForeignMethodFn setter, const bool isStatic)
: name(std::move(name)), getter(getter), setter(setter), isStatic(isStatic) {
}
virtual ~ForeignProp() = default;
// If you are getting "referencing deleted function" error which is this one below,
// then you are trying to make a copy of ForeignKlass during vm.klass<Foo>("Foo");
ForeignProp(const ForeignProp& other) = delete;
void generate(std::ostream& os) const {
if (getter)
os << " foreign " << name << "\n";
if (setter)
os << " foreign " << name << "=(rhs)\n";
}
const std::string& getName() const {
return name;
}
WrenForeignMethodFn getSetter() {
return setter;
}
WrenForeignMethodFn getGetter() {
return getter;
}
bool getStatic() const {
return isStatic;
}
protected:
std::string name;
WrenForeignMethodFn getter;
WrenForeignMethodFn setter;
bool isStatic;
};
class ForeignKlass {
public:
ForeignKlass(std::string name) : name(std::move(name)), allocators{nullptr, nullptr} {
}
virtual ~ForeignKlass() = default;
// If you are getting "referencing deleted function" error which is this one below,
// then you are trying to make a copy of this class.
ForeignKlass(const ForeignKlass& other) = delete;
virtual void generate(std::ostream& os) const = 0;
ForeignMethod& findFunc(const std::string& name, const bool isStatic) {
const auto it = methods.find(name);
if (it == methods.end())
throw NotFound();
if (it->second->getStatic() != isStatic)
throw NotFound();
return *it->second;
}
ForeignProp& findProp(const std::string& name, const bool isStatic) {
const auto it = props.find(name);
if (it == props.end())
throw NotFound();
if (it->second->getStatic() != isStatic)
throw NotFound();
return *it->second;
}
WrenForeignMethodFn findSignature(const std::string& signature, const bool isStatic) {
switch (signature[0]) {
case '[':
case '-':
case '+':
case '/':
case '*':
case '=':
case '!':
case '%':
case '<':
case '>':
case '&':
case '^':
case '|': {
// Operators
return findFunc(signature, isStatic).getMethod();
}
default: {
if (signature.find('(') != std::string::npos) {
// Check if setter
if (signature.find("=(_)") != std::string::npos) {
return findProp(signature.substr(0, signature.find_first_of('=')), isStatic).getSetter();
} else {
// Must be a method
return findFunc(signature, isStatic).getMethod();
}
} else {
return findProp(signature, isStatic).getGetter();
}
}
}
}
const std::string& getName() const {
return name;
}
WrenForeignClassMethods& getAllocators() {
return allocators;
}
protected:
std::string name;
std::string ctorDef;
std::unordered_map<std::string, std::unique_ptr<ForeignMethod>> methods;
std::unordered_map<std::string, std::unique_ptr<ForeignProp>> props;
WrenForeignClassMethods allocators;
};
template <typename... Args> class ForeignMethodImpl : public ForeignMethod {
public:
ForeignMethodImpl(std::string name, std::string signature, WrenForeignMethodFn fn, const bool isStatic)
: ForeignMethod(std::move(name), fn, isStatic), signature(std::move(signature)) {
}
~ForeignMethodImpl() = default;
void generate(std::ostream& os) const override {
os << " foreign " << (isStatic ? "static " : "") << signature << "\n";
}
static std::string generateSignature(const std::string& name) {
std::stringstream os;
constexpr auto n = sizeof...(Args);
os << name << "(";
for (size_t i = 0; i < n; i++) {
if (i == 0)
os << "arg0";
else
os << ", arg" << i;
}
os << ")";
return os.str();
}
static std::string generateSignature(const ForeignMethodOperator name) {
switch (name) {
case OPERATOR_GET_INDEX:
return "[arg]";
case OPERATOR_SET_INDEX:
return "[arg]=(rhs)";
case OPERATOR_ADD:
return "+(rhs)";
case OPERATOR_SUB:
return "-(rhs)";
case OPERATOR_DIV:
return "/(rhs)";
case OPERATOR_MUL:
return "*(rhs)";
case OPERATOR_MOD:
return "%(rhs)";
case OPERATOR_EQUAL:
return "==(rhs)";
case OPERATOR_NOT_EQUAL:
return "!=(rhs)";
case OPERATOR_NEG:
return "-";
case OPERATOR_GT:
return ">(rhs)";
case OPERATOR_LT:
return "<(rhs)";
case OPERATOR_GT_EQUAL:
return ">=(rhs)";
case OPERATOR_LT_EQUAL:
return "<=(rhs)";
case OPERATOR_SHIFT_LEFT:
return "<<(rhs)";
case OPERATOR_SHIFT_RIGHT:
return ">>(rhs)";
case OPERATOR_AND:
return "&(rhs)";
case OPERATOR_XOR:
return "^(rhs)";
case OPERATOR_OR:
return "|(rhs)";
default:
throw Exception("Operator not supported");
}
}
static std::string generateName(const ForeignMethodOperator name) {
switch (name) {
case OPERATOR_GET_INDEX:
return "[_]";
case OPERATOR_SET_INDEX:
return "[_]=(_)";
case OPERATOR_ADD:
return "+(_)";
case OPERATOR_SUB:
return "-(_)";
case OPERATOR_DIV:
return "/(_)";
case OPERATOR_MUL:
return "*(_)";
case OPERATOR_MOD:
return "%(_)";
case OPERATOR_EQUAL:
return "==(_)";
case OPERATOR_NOT_EQUAL:
return "!=(_)";
case OPERATOR_NEG:
return "-";
case OPERATOR_GT:
return ">(_)";
case OPERATOR_LT:
return "<(_)";
case OPERATOR_GT_EQUAL:
return ">=(_)";
case OPERATOR_LT_EQUAL:
return "<=(_)";
case OPERATOR_SHIFT_LEFT:
return "<<(_)";
case OPERATOR_SHIFT_RIGHT:
return ">>(_)";
case OPERATOR_AND:
return "&(_)";
case OPERATOR_XOR:
return "^(_)";
case OPERATOR_OR:
return "|(_)";
default:
throw Exception("Operator not supported");
}
}
private:
std::string signature;
};
template <typename T, typename V> class ForeignPropImpl : public ForeignProp {
public:
ForeignPropImpl(std::string name, WrenForeignMethodFn getter, WrenForeignMethodFn setter, const bool isStatic)
: ForeignProp(std::move(name), getter, setter, isStatic) {
}
~ForeignPropImpl() = default;
};
#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace detail {
template <typename... Args> inline std::string generateNameArgs() {
constexpr auto n = sizeof...(Args);
std::stringstream ss;
ss << "(";
for (size_t i = 0; i < n; i++) {
ss << "_";
if (i != n - 1) {
ss << ",";
}
}
ss << ")";
return ss.str();
}
template <typename Signature, Signature signature> struct ForeignFunctionDetails;
template <typename R, typename... Args, R (*Fn)(Args...)> struct ForeignFunctionDetails<R (*)(Args...), Fn> {
typedef ForeignMethodImpl<Args...> ForeignMethodImplType;
static std::unique_ptr<ForeignMethodImplType> make(std::string name) {
auto signature = ForeignMethodImplType::generateSignature(name);
auto p = detail::ForeignFunctionCaller<R, Args...>::template call<Fn>;
name = name + detail::generateNameArgs<Args...>();
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, true);
}
};
template <typename M> struct GetPointerType {
template <typename C, typename T> static T getType(T C::*v);
typedef decltype(getType(static_cast<M>(nullptr))) type;
};
template <typename Var, Var var>
struct GetVarTraits;
template <typename C, typename T, T C::*Var> struct GetVarTraits<T C::*, Var> {
using klass = C;
};
} // namespace detail
#endif
template <typename T> class ForeignKlassImpl : public ForeignKlass {
public:
ForeignKlassImpl(std::string name) : ForeignKlass(std::move(name)) {
allocators.allocate = nullptr;
allocators.finalize = &detail::ForeignKlassAllocator<T>::finalize;
}
~ForeignKlassImpl() = default;
// If you are getting "referencing deleted function" error which is this one below,
// then you are trying to make a copy of this class.
// Make sure you are not creating a copy while registering your custom C++ class as:
// auto& cls = vm.module("mymodule").klass<Foo>("Foo");
// Notice the "auto&"
ForeignKlassImpl(const ForeignKlassImpl<T>& other) = delete;
void generate(std::ostream& os) const override {
os << "foreign class " << name << " {\n";
if (!ctorDef.empty()) {
os << " " << ctorDef;
}
for (const auto& pair : methods) {
pair.second->generate(os);
}
for (const auto& pair : props) {
pair.second->generate(os);
}
os << "}\n\n";
}
template <typename... Args> void ctor(const std::string& name = "new") {
allocators.allocate = &detail::ForeignKlassAllocator<T, Args...>::allocate;
std::stringstream ss;
ss << "construct " << name << " (";
constexpr auto n = sizeof...(Args);
for (size_t i = 0; i < n; i++) {
if (i == 0)
ss << "arg0";
else
ss << ", arg" << i;
}
ss << ") {}\n\n";
ctorDef = ss.str();
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename Signature, Signature signature> struct ForeignMethodDetails;
template <typename R, typename C, typename... Args, R (C::*Fn)(Args...)>
struct ForeignMethodDetails<R (C::*)(Args...), Fn> {
static_assert(std::is_base_of<C, T>::value, "The method belong to its own class or a base class");
typedef ForeignMethodImpl<Args...> ForeignMethodImplType;
static std::unique_ptr<ForeignMethodImplType> make(std::string name) {
auto signature = ForeignMethodImplType::generateSignature(name);
auto p = detail::ForeignMethodCaller<R, C, Args...>::template call<Fn>;
name = name + detail::generateNameArgs<Args...>();
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
static std::unique_ptr<ForeignMethodImplType> make(const ForeignMethodOperator op) {
auto signature = ForeignMethodImplType::generateSignature(op);
auto name = ForeignMethodImplType::generateName(op);
auto p = detail::ForeignMethodCaller<R, C, Args...>::template call<Fn>;
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
};
template <typename R, typename C, typename... Args, R (C::*Fn)(Args...) const>
struct ForeignMethodDetails<R (C::*)(Args...) const, Fn> {
static_assert(std::is_base_of<C, T>::value, "The method belong to its own class or a base class");
typedef ForeignMethodImpl<Args...> ForeignMethodImplType;
static std::unique_ptr<ForeignMethodImplType> make(std::string name) {
auto signature = ForeignMethodImplType::generateSignature(name);
auto p = detail::ForeignMethodCaller<R, C, Args...>::template call<Fn>;
name = name + detail::generateNameArgs<Args...>();
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
static std::unique_ptr<ForeignMethodImplType> make(const ForeignMethodOperator op) {
auto signature = ForeignMethodImplType::generateSignature(op);
auto name = ForeignMethodImplType::generateName(op);
auto p = detail::ForeignMethodCaller<R, C, Args...>::template call<Fn>;
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
};
template <typename Signature, Signature signature> struct ForeignMethodExtDetails;
template <typename R, typename... Args, R (*Fn)(T&, Args...)>
struct ForeignMethodExtDetails<R (*)(T&, Args...), Fn> {
typedef ForeignMethodImpl<Args...> ForeignMethodImplType;
static std::unique_ptr<ForeignMethodImplType> make(std::string name) {
auto signature = ForeignMethodImplType::generateSignature(name);
auto p = detail::ForeignMethodExtCaller<R, T, Args...>::template call<Fn>;
name = name + detail::generateNameArgs<Args...>();
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
static std::unique_ptr<ForeignMethodImplType> make(const ForeignMethodOperator op) {
auto signature = ForeignMethodImplType::generateSignature(op);
auto name = ForeignMethodImplType::generateName(op);
auto p = detail::ForeignMethodExtCaller<R, T, Args...>::template call<Fn>;
return std::make_unique<ForeignMethodImplType>(std::move(name), std::move(signature), p, false);
}
};
template <typename V, typename C, V C::*Ptr> struct ForeignVarDetails {
static_assert(std::is_base_of<C, T>::value, "The variable belong to its own class or a base class");
static std::unique_ptr<ForeignProp> make(std::string name, const bool readonly) {
auto s = readonly ? nullptr : detail::ForeignPropCaller<C, V, Ptr>::setter;
auto g = detail::ForeignPropCaller<C, V, Ptr>::getter;
return std::make_unique<ForeignProp>(std::move(name), g, s, false);
}
};
template <typename V, typename C, V C::*Ptr> struct ForeignVarReadonlyDetails {
static_assert(std::is_base_of<C, T>::value, "The variable belong to its own class or a base class");
static std::unique_ptr<ForeignProp> make(std::string name, const bool readonly) {
auto g = detail::ForeignPropCaller<C, V, Ptr>::getter;
return std::make_unique<ForeignProp>(std::move(name), g, nullptr, false);
}
};
template <typename Signature, Signature signature> struct ForeignSetterDetails;
template <typename V, typename C, void (C::*Fn)(V)> struct ForeignSetterDetails<void (C::*)(V), Fn> {
static_assert(std::is_base_of<C, T>::value, "The setter must belong to its own class or a base class");
static WrenForeignMethodFn method() {
return detail::ForeignMethodCaller<void, C, V>::template call<Fn>;
}
};
template <typename Signature, Signature signature> struct ForeignSetterExtDetails;
template <typename V, void (*Fn)(T&, V)> struct ForeignSetterExtDetails<void (*)(T&, V), Fn> {
static WrenForeignMethodFn method() {
return detail::ForeignMethodExtCaller<void, T, V>::template call<Fn>;
}
};
template <typename Signature, Signature signature> struct ForeignGetterDetails;
template <typename R, typename C, R (C::*Fn)()> struct ForeignGetterDetails<R (C::*)(), Fn> {
static_assert(std::is_base_of<C, T>::value, "The getter must belong to its own class or a base class");
static WrenForeignMethodFn method() {
return detail::ForeignMethodCaller<R, C>::template call<Fn>;
}
};
template <typename R, typename C, R (C::*Fn)() const> struct ForeignGetterDetails<R (C::*)() const, Fn> {
static_assert(std::is_base_of<C, T>::value, "The getter must belong to its own class or a base class");
static WrenForeignMethodFn method() {
return detail::ForeignMethodCaller<R, C>::template call<Fn>;
}
};
template <typename Signature, Signature signature> struct ForeignGetterExtDetails;
template <typename R, R (*Fn)(T&)> struct ForeignGetterExtDetails<R (*)(T&), Fn> {
static WrenForeignMethodFn method() {
return detail::ForeignMethodExtCaller<R, T>::template call<Fn>;
}
};
#endif
template <auto Fn> void func(std::string name) {
auto ptr = ForeignMethodDetails<decltype(Fn), Fn>::make(std::move(name));
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Fn> void func(const ForeignMethodOperator name) {
auto ptr = ForeignMethodDetails<decltype(Fn), Fn>::make(name);
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Fn> void funcExt(std::string name) {
auto ptr = ForeignMethodExtDetails<decltype(Fn), Fn>::make(std::move(name));
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Fn> void funcExt(const ForeignMethodOperator name) {
auto ptr = ForeignMethodExtDetails<decltype(Fn), Fn>::make(name);
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Fn> void funcStatic(std::string name) {
auto ptr = detail::ForeignFunctionDetails<decltype(Fn), Fn>::make(std::move(name));
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Fn> void funcStaticExt(std::string name) {
// This is exactly the same as funcStatic because there is
// no difference for "static void Foo::foo(){}" and "void foo(){}"!
auto ptr = detail::ForeignFunctionDetails<decltype(Fn), Fn>::make(std::move(name));
methods.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Var> void var(std::string name) {
using R = typename detail::GetPointerType<decltype(Var)>::type;
using C = typename detail::GetVarTraits<decltype(Var), Var>::klass;
auto ptr = ForeignVarDetails<R, C, Var>::make(std::move(name), false);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Var> void varReadonly(std::string name) {
using R = typename detail::GetPointerType<decltype(Var)>::type;
using C = typename detail::GetVarTraits<decltype(Var), Var>::klass;
auto ptr = ForeignVarReadonlyDetails<R, C, Var>::make(std::move(name), true);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Getter, auto Setter> void prop(std::string name) {
auto g = ForeignGetterDetails<decltype(Getter), Getter>::method();
auto s = ForeignSetterDetails<decltype(Setter), Setter>::method();
auto ptr = std::make_unique<ForeignProp>(std::move(name), g, s, false);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Getter> void propReadonly(std::string name) {
auto g = ForeignGetterDetails<decltype(Getter), Getter>::method();
auto ptr = std::make_unique<ForeignProp>(std::move(name), g, nullptr, false);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Getter, auto Setter> void propExt(std::string name) {
auto g = ForeignGetterExtDetails<decltype(Getter), Getter>::method();
auto s = ForeignSetterExtDetails<decltype(Setter), Setter>::method();
auto ptr = std::make_unique<ForeignProp>(std::move(name), g, s, false);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
template <auto Getter> void propReadonlyExt(std::string name) {
auto g = ForeignGetterExtDetails<decltype(Getter), Getter>::method();
auto ptr = std::make_unique<ForeignProp>(std::move(name), g, nullptr, false);
props.insert(std::make_pair(ptr->getName(), std::move(ptr)));
}
};
} // namespace wrenbind17