wrenpp/include/wrenpp/vm_fun.hpp

321 lines
12 KiB
C++

/* Copyright 2020, Michele Santullo
* This file is part of wrenpp.
*
* Wrenpp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Wrenpp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wrenpp. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "vm.hpp"
#include "string_bt.hpp"
#include <string>
#include <string_view>
#include <vector>
#include <type_traits>
#include <cstddef>
namespace wren {
typedef std::tuple<const char*, const char*> ModuleAndName;
namespace detail {
template <typename T> struct GetTypeToRetType;
template <typename T> using GetTypeToRetType_t = typename GetTypeToRetType<T>::type;
#if __cpp_concepts >= 201907
template <typename T>
concept ConstCharTuple = requires {
std::same_as<std::remove_cv_t<T>, Handle> or
std::same_as<std::remove_cv_t<T>, ModuleAndName>;
};
#endif
} //namespace detail
#if __cpp_concepts >= 201907
template <typename... Outs, detail::ConstCharTuple... Params>
#else
template <typename... Outs, typename... Params>
#endif
std::tuple<detail::GetTypeToRetType_t<Outs>...> variables(VM& vm, Params&&... modules_names);
template <typename R, typename... Args>
R call (VM& vm, const Handle& object, const Handle& method, const Args&... args);
template <typename R, typename... Args>
R call (VM& vm, const ModuleAndName& object, const Handle& method, const Args&... args);
template <typename R, std::size_t N, typename... Args>
R call (VM& vm, const Handle& object, const char (&method)[N], const Args&... args);
template <typename R, std::size_t N, typename... Args>
R call (VM& vm, const ModuleAndName& object, const char (&method)[N], const Args&... args);
template <typename T>
foreign_class_t make_foreign_class();
template <auto V>
foreign_method_t make_method_bindable();
void interpret (VM& vm, const std::string& module_name, const std::string& script);
void set (VM& vm, int slot_num, const char* value);
void set (VM& vm, int slot_num, double value);
void set (VM& vm, int slot_num, bool value);
void set (VM& vm, int slot_num, std::nullptr_t);
void set (VM& vm, int slot_num, const char* bytes, std::size_t length);
void set (VM& vm, int slot_num, const std::string& value);
void set (VM& vm, int slot_num, std::string_view value);
void set (VM& vm, int slot_num, const std::vector<char>& value);
void set (VM& vm, int slot_num, const char* beg, const char* end);
void set (VM& vm, int slot_num, int value);
void set (VM& vm, int slot_num, std::size_t value);
std::string_view slot_string_view (VM& vm, int slot_num);
template <typename T> detail::GetTypeToRetType_t<T> get (VM& vm, int slot_num);
template <typename T> T* foreign (VM& vm, int slot_num);
void variable(VM& vm, const ModuleAndName& mod_and_name, int slot);
void variable(VM& vm, const Handle& handle, int slot);
void variable_ensure_slot(VM& vm, const ModuleAndName& mod_and_name, int slot);
void variable_ensure_slot(VM& vm, const Handle& handle, int slot);
namespace detail {
template <typename T> struct TType { typedef T type; };
template<typename T>struct GetTypeToRetType{
static_assert(not std::is_fundamental_v<T>, "User type expected");
static_assert(not std::is_pointer_v<T>, "Unexpected pointer type");
typedef std::remove_cv_t<T>* type;
};
template<>struct GetTypeToRetType<const char*> : TType<const char*>{};
template<>struct GetTypeToRetType<double> : TType<double>{};
template<>struct GetTypeToRetType<bool> : TType<bool>{};
template<>struct GetTypeToRetType<std::pair<const char*,int>> : TType<std::pair<const char*,int>> {};
template<>struct GetTypeToRetType<int> : TType<int>{};
template<>struct GetTypeToRetType<std::size_t> : TType<std::size_t>{};
template<>struct GetTypeToRetType<std::string> : TType<std::string>{};
template<>struct GetTypeToRetType<std::string_view> : TType<std::string_view>{};
template<>struct GetTypeToRetType<std::vector<char>> : TType<std::vector<char>>{};
#if __cpp_concepts >= 201907
template <typename... Outs, int... Indices, ConstCharTuple... Params>
#else
template <typename... Outs, int... Indices, typename... Params>
#endif
inline std::tuple<detail::GetTypeToRetType_t<Outs>...> variables_impl (
VM& vm,
std::integer_sequence<int, Indices...>,
Params&&... modules_names
) {
if constexpr (sizeof...(Outs) == 0) {
return {};
}
else {
static_assert(sizeof...(Params) == sizeof...(Outs), "Expected a module/name pair per requested output");
static_assert(sizeof...(Outs) == sizeof...(Indices), "Mismatching index count");
vm.ensure_slots(sizeof...(Params));
(variable(vm, modules_names, Indices), ...);
return std::tuple<detail::GetTypeToRetType_t<Outs>...>(get<Outs>(vm, Indices)...);
}
}
template <typename T>
inline void set_single_for_call (VM& vm, int slot, const T& value) {
set(vm, slot, value);
}
template <>
inline void set_single_for_call (VM& vm, int slot_num, const Handle& handle) {
vm.set_slot_handle(handle, slot_num);
}
template <>
inline void set_single_for_call (VM& vm, int slot_num, const ModuleAndName& name) {
variable(vm, name, slot_num);
}
template <typename... Args, int... Indices>
inline void set_for_call (std::integer_sequence<int, Indices...>, VM& vm, const Args&... args) {
vm.ensure_slots(sizeof...(Args));
(set_single_for_call(vm, Indices + 1, args), ...);
}
template <typename T, typename R, auto Method, typename... Args> struct MakeMethodBindableConstNonConst {
private:
template <int... Indices>
static R call_implem (std::integer_sequence<int, Indices...>, VM& vm) {
static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count");
T* obj = foreign<T>(vm, 0);
return (obj->*Method)(get<std::decay_t<Args>>(vm, Indices + 1)...);
}
public:
static foreign_method_t make() {
return [](VM& vm) {
if constexpr (std::is_same_v<R, void>) {
call_implem(std::make_integer_sequence<int, sizeof...(Args)>(), vm);
}
else {
auto ret = call_implem(std::make_integer_sequence<int, sizeof...(Args)>(), vm);
vm.ensure_slots(1);
set(vm, 0, ret);
}
};
}
};
template <auto V> struct MakeMethodBindable;
template <typename T, void(T::*Method)(VM&)> struct MakeMethodBindable<Method> {
static foreign_method_t make() {
return [](VM& vm) {
T* obj = foreign<T>(vm, 0);
(obj->*Method)(vm);
};
}
};
template <typename T, typename R, typename... Args, R(T::*Method)(Args...)> struct MakeMethodBindable<Method> : MakeMethodBindableConstNonConst<T, R, Method, Args...> {};
template <typename T, typename R, typename... Args, R(T::*Method)(Args...)const> struct MakeMethodBindable<Method> : MakeMethodBindableConstNonConst<T, R, Method, Args...> {};
} //namespace detail
#if __cpp_concepts >= 201907
template <typename... Outs, detail::ConstCharTuple... Params>
#else
template <typename... Outs, typename... Params>
#endif
inline std::tuple<detail::GetTypeToRetType_t<Outs>...> variables(VM& vm, Params&&... modules_names) {
return detail::variables_impl<Outs...>(vm, std::make_integer_sequence<int, sizeof...(Outs)>(), std::forward<Params>(modules_names)...);
}
inline void interpret (VM& vm, const std::string& module_name, const std::string& script) {
return vm.interpret(module_name.c_str(), script.c_str());
}
inline void set (VM& vm, int slot_num, const char* value) {
vm.set_slot_string(slot_num, value);
}
inline void set (VM& vm, int slot_num, double value) {
vm.set_slot_double(slot_num, value);
}
inline void set (VM& vm, int slot_num, bool value) {
vm.set_slot_bool(slot_num, value);
}
inline void set (VM& vm, int slot_num, std::nullptr_t) {
vm.set_slot_null(slot_num);
}
inline void set (VM& vm, int slot_num, const char* bytes, std::size_t length) {
vm.set_slot_bytes(slot_num, bytes, length);
}
inline void set (VM& vm, int slot_num, const std::string& value) {
vm.set_slot_string(slot_num, value.c_str());
}
inline void set (VM& vm, int slot_num, std::string_view value) {
vm.set_slot_bytes(slot_num, value.data(), value.size());
}
inline void set (VM& vm, int slot_num, const std::vector<char>& value) {
vm.set_slot_bytes(slot_num, value.data(), value.size());
}
inline void set (VM& vm, int slot_num, int value) {
vm.set_slot_double(slot_num, static_cast<double>(value));
}
inline void set (VM& vm, int slot_num, std::size_t value) {
vm.set_slot_double(slot_num, static_cast<double>(value));
}
template <typename T>
inline T* foreign (VM& vm, int slot_num) {
T* obj = static_cast<T*>(vm.slot_foreign(slot_num));
return obj;
}
template <typename R, typename... Args>
inline R call (VM& vm, const Handle& object, const Handle& method, const Args&... args) {
detail::set_for_call<Args...>(std::make_integer_sequence<int, sizeof...(Args)>(), vm, args...);
vm.call(method);
if constexpr (not std::is_same<void, R>::value) {
return get<R>(vm, 0);
}
}
template <typename R, typename... Args>
inline R call (VM& vm, const ModuleAndName& object, const Handle& method, const Args&... args) {
vm.ensure_slots(sizeof...(args) + 1);
variable(vm, object, 0);
Handle obj_handle = vm.slot_handle(0);
return call<R, Args...>(vm, obj_handle, method, args...);
}
template <typename R, std::size_t N, typename... Args>
inline R call (VM& vm, const Handle& object, const char (&method)[N], const Args&... args) {
const constexpr char dummy_name_buff[N] = {0};
const constexpr auto params = dhandy::bt::string<N, char>(dummy_name_buff) +
dhandy::bt::make_string("(") +
((static_cast<void>(args), dhandy::bt::make_string(",_")) + ... + dhandy::bt::make_string("")).template substr<1>() +
dhandy::bt::make_string(")");
;
char cat_buff[params.size() + 1];
std::copy(method, method + N - 1, cat_buff);
std::copy(params.data() + N - 1, params.data() + params.size() + 1, cat_buff + N - 1);
return call<R, Args...>(vm, object, vm.make_call_handle(cat_buff), args...);
}
template <typename R, std::size_t N, typename... Args>
inline R call (VM& vm, const ModuleAndName& object, const char (&method)[N], const Args&... args) {
vm.ensure_slots(sizeof...(args) + 1);
variable(vm, object, 0);
Handle obj_handle = vm.slot_handle(0);
return call<R, N, Args...>(vm, obj_handle, method, args...);
}
template <typename T>
inline foreign_class_t make_foreign_class() {
foreign_class_t ret;
ret.allocate = [](VM& vm) {
void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T));
new(mem) T;
};
ret.finalize = [](void* mem) {
const auto cale = static_cast<T*>(mem);
cale->~T();
};
return ret;
}
template <auto V>
inline foreign_method_t make_method_bindable() {
return detail::MakeMethodBindable<V>::make();
}
template <typename T>
inline detail::GetTypeToRetType_t<T> get (VM& vm, int slot_num) {
return foreign<T>(vm, slot_num);
}
template<> const char* get<const char*> (VM& vm, int slot_num);
template <> double get<double> (VM& vm, int slot_num);
template <> bool get<bool> (VM& vm, int slot_num);
template <> std::pair<const char*, int> get<std::pair<const char*,int>> (VM& vm, int slot_num);
template<> int get<int> (VM& vm, int slot_num);
template<> std::size_t get<std::size_t> (VM& vm, int slot_num);
template<> std::string get<std::string> (VM& vm, int slot_num);
template <> std::string_view get<std::string_view> (VM& vm, int slot_num);
template <> std::vector<char> get<std::vector<char>> (VM& vm, int slot_num);
} //namespace wren