Add support for registering foreign methods that return foreign objects
Still a bit work in progress but functioning
This commit is contained in:
parent
9c147b1a6e
commit
1fa6d62f17
15 changed files with 264 additions and 45 deletions
|
@ -123,7 +123,7 @@ int main() {
|
|||
vm.callback_manager().add_callback(true, "calendar", "Calendar", "today()", &Calendar::today)
|
||||
.add_callback(false, "calendar", "Calendar", "add_appointment(_)", wren::make_method_bindable<&Calendar::add_appointment>())
|
||||
.add_callback(false, "calendar", "Calendar", "appointment_count()", wren::make_method_bindable<&Calendar::appointment_count>());
|
||||
vm.class_manager().add_class_maker("calendar", "Calendar", &wren::make_foreign_class<Calendar>);
|
||||
vm.class_manager().add_class_maker("calendar", "Calendar", wren::make_foreign_class<Calendar>);
|
||||
|
||||
vm.interpret("main", g_test_script);
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ int main() {
|
|||
.add_callback(false, "math_vector", "MathVector", "z", make_method_bindable<&Vector<double>::z>());
|
||||
|
||||
vm.class_manager()
|
||||
.add_class_maker("math_vector", "MathVector", &make_foreign_class<Vector<double>,
|
||||
.add_class_maker("math_vector", "MathVector", make_foreign_class<Vector<double>,
|
||||
double, //single value constructor
|
||||
std::tuple<double, double, double>, //x,y,z constructor
|
||||
std::tuple<> //default constructor
|
||||
|
|
|
@ -19,10 +19,13 @@
|
|||
|
||||
#include "detail/wren_types.hpp"
|
||||
#include "detail/strings_in_vector.hpp"
|
||||
#include "detail/construct_foreign_class.hpp"
|
||||
#include "detail/wren_class_name_from_type.hpp"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace wren {
|
||||
namespace detail {
|
||||
|
@ -47,6 +50,7 @@ namespace wren {
|
|||
|
||||
std::string_view module_name() const { return this->as_string_view<0>(); }
|
||||
std::string_view class_name() const { return this->as_string_view<1>(); }
|
||||
operator ModuleAndName() const;
|
||||
|
||||
using StringsInVector<2>::operator<;
|
||||
bool operator== (const ClassNameOwning& other) const;
|
||||
|
@ -58,6 +62,9 @@ namespace wren {
|
|||
TempClassName (const ClassNameOwning& model);
|
||||
bool operator== (const TempClassName& other) const;
|
||||
|
||||
std::string_view module_name() const { return m_module_name; }
|
||||
std::string_view class_name() const { return m_class_name; }
|
||||
|
||||
private:
|
||||
std::string_view m_module_name, m_class_name;
|
||||
};
|
||||
|
@ -71,19 +78,73 @@ namespace wren {
|
|||
using is_transparent = std::true_type;
|
||||
std::size_t operator()(TempClassName value) const;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct CppClassTypeHelper {
|
||||
static void register_ifp(ClassManager&, const ClassNameOwning* name);
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct CppClassTypeHelper<MakeForeignClass<T, Args...>> {
|
||||
static void register_ifp(ClassManager& classman, const ClassNameOwning* name);
|
||||
};
|
||||
} //namespace detail
|
||||
|
||||
class ClassManager {
|
||||
template <typename F> friend struct detail::CppClassTypeHelper;
|
||||
typedef std::function<foreign_class_t()> make_foreign_class_t;
|
||||
typedef std::unordered_map<detail::ClassNameOwning, make_foreign_class_t, detail::ClassNameHash, detail::ClassNameEqual> ClassMap;
|
||||
typedef std::unordered_map<ClassManagerNameHashType, const detail::ClassNameOwning*> TypeMap;
|
||||
public:
|
||||
ClassManager();
|
||||
~ClassManager() noexcept;
|
||||
|
||||
ClassManager& add_class_maker (std::string_view module_name, std::string_view class_name, make_foreign_class_t);
|
||||
template <typename F>
|
||||
ClassManager& add_class_maker (std::string module_name, std::string_view class_name, F&& maker);
|
||||
foreign_class_t make_class(std::string_view module_name, std::string_view class_name);
|
||||
ModuleAndName wren_class_name_from_hash(ClassManagerNameHashType hash) const;
|
||||
template <typename T>
|
||||
ModuleAndName wren_class_name_from_type() const;
|
||||
|
||||
private:
|
||||
ClassMap::const_iterator add_class_maker_implem (
|
||||
std::string_view module_name,
|
||||
std::string_view class_name,
|
||||
make_foreign_class_t
|
||||
);
|
||||
|
||||
void add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name);
|
||||
|
||||
ClassMap m_classes;
|
||||
TypeMap m_types; //refers to memory in m_classes
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename F>
|
||||
inline void CppClassTypeHelper<F>::register_ifp(ClassManager&, const ClassNameOwning*) {
|
||||
//Nothing to do
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline void CppClassTypeHelper<MakeForeignClass<T, Args...>>::register_ifp(
|
||||
ClassManager& classman,
|
||||
const ClassNameOwning* name
|
||||
) {
|
||||
const auto new_hash = type_hash<T>();
|
||||
classman.add_type(new_hash, name);
|
||||
}
|
||||
} //namespace detail
|
||||
|
||||
template <typename F>
|
||||
inline ClassManager& ClassManager::add_class_maker (std::string module_name, std::string_view class_name, F&& maker) {
|
||||
auto it_new = this->add_class_maker_implem(module_name, class_name, std::forward<F>(maker));
|
||||
const auto& owning_name = it_new->first;
|
||||
detail::CppClassTypeHelper<std::decay_t<F>>::register_ifp(*this, &owning_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ModuleAndName ClassManager::wren_class_name_from_type() const {
|
||||
return detail::wren_class_name_from_type<T>(*this);
|
||||
}
|
||||
} //namespace wren
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "wrenpp/vm.hpp"
|
||||
#include "../vm.hpp"
|
||||
#include "setters_getters.hpp"
|
||||
#if defined(WRENPP_WITH_NAME_GUESSING)
|
||||
# include "guess_class_name.hpp"
|
||||
|
@ -123,33 +123,46 @@ namespace wren {
|
|||
static_assert(dummy == 1); //always true
|
||||
}
|
||||
}
|
||||
|
||||
//This is named from the perspective of the user - they say
|
||||
//MakeForeignClass<X> and it will "produce" object of type X. In
|
||||
//reality it produces a functor that does that, and it's that functor
|
||||
//that gets registered into wren, so from the library perspective it
|
||||
//should be called MakeForeignClassMaker
|
||||
template <typename T, typename... Args>
|
||||
class MakeForeignClass {
|
||||
public:
|
||||
MakeForeignClass() = default;
|
||||
|
||||
foreign_class_t operator()() const {
|
||||
detail::static_assert_all_packs_are_unique<Args...>();
|
||||
|
||||
foreign_class_t ret;
|
||||
ret.allocate = [](VM& vm, ModuleAndName mn) {
|
||||
void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T));
|
||||
|
||||
const auto result = detail::ForeignClassHelper<T, Args...>::construct(vm, vm.slot_count() - 1, mem);
|
||||
if (not result.success) {
|
||||
set(vm, 1, std::string{"No registered c++ constructor "} +
|
||||
#if defined(WRENPP_WITH_NAME_GUESSING)
|
||||
"for class " + guess_class_name<T>() + " " +
|
||||
#endif
|
||||
"takes " + std::to_string(result.parameter_count) +
|
||||
" parameter" + (result.parameter_count == 1 ? "" : "s")
|
||||
);
|
||||
vm.abort_fiber(1);
|
||||
}
|
||||
};
|
||||
ret.finalize = [](void* mem) {
|
||||
const auto cale = static_cast<T*>(mem);
|
||||
cale->~T();
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
} //namespace detail
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline foreign_class_t make_foreign_class() {
|
||||
detail::static_assert_all_packs_are_unique<Args...>();
|
||||
|
||||
foreign_class_t ret;
|
||||
ret.allocate = [](VM& vm, ModuleAndName mn) {
|
||||
void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T));
|
||||
|
||||
const auto result = detail::ForeignClassHelper<T, Args...>::construct(vm, vm.slot_count() - 1, mem);
|
||||
if (not result.success) {
|
||||
set(vm, 1, std::string{"No registered c++ constructor "} +
|
||||
#if defined(WRENPP_WITH_NAME_GUESSING)
|
||||
"for class " + guess_class_name<T>() + " " +
|
||||
#endif
|
||||
"takes " + std::to_string(result.parameter_count) +
|
||||
" parameter" + (result.parameter_count == 1 ? "" : "s")
|
||||
);
|
||||
vm.abort_fiber(1);
|
||||
}
|
||||
};
|
||||
ret.finalize = [](void* mem) {
|
||||
const auto cale = static_cast<T*>(mem);
|
||||
cale->~T();
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
constexpr detail::MakeForeignClass<T, Args...> make_foreign_class;
|
||||
} //namespace wren
|
||||
|
|
|
@ -8,6 +8,7 @@ include_files = [
|
|||
'string_bt.hpp',
|
||||
'StringCRC32.hpp',
|
||||
'strings_in_vector.hpp',
|
||||
'wren_class_name_from_type.hpp',
|
||||
'wren_types.hpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace wren {
|
|||
//spits out.
|
||||
class ModuleAndName {
|
||||
public:
|
||||
constexpr ModuleAndName();
|
||||
constexpr ModuleAndName (const char* base, std::uint32_t hash, std::size_t mod_name_len, std::size_t class_name_len);
|
||||
|
||||
constexpr const char* module_name_char() const noexcept { return m_base; }
|
||||
|
@ -73,6 +74,10 @@ namespace wren {
|
|||
static_assert(sizeof(raw) == sizeof(module_and_name));
|
||||
};
|
||||
|
||||
constexpr ModuleAndName::ModuleAndName() :
|
||||
ModuleAndName(nullptr, 0, 0, 0)
|
||||
{}
|
||||
|
||||
constexpr ModuleAndName::ModuleAndName (const char* base, std::uint32_t hash, std::size_t mod_name_len, std::size_t class_name_len) :
|
||||
m_base(base),
|
||||
m_hash(hash),
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "wrenpp/vm.hpp"
|
||||
#include "../vm.hpp"
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
|
|
@ -81,6 +81,10 @@ namespace wren {
|
|||
bool operator== (const StringsInVectorImplem& other) const;
|
||||
bool operator< (const StringsInVectorImplem& other) const;
|
||||
|
||||
protected:
|
||||
const char* raw_buffer() const;
|
||||
std::size_t raw_buffer_size() const;
|
||||
|
||||
private:
|
||||
std::vector<char> m_memory;
|
||||
};
|
||||
|
@ -132,6 +136,16 @@ namespace wren {
|
|||
return this_memory < other_memory;
|
||||
}
|
||||
|
||||
template <std::size_t Count, std::size_t... Indices>
|
||||
inline const char* StringsInVectorImplem<Count, std::index_sequence<Indices...>>::raw_buffer() const {
|
||||
return m_memory.data();
|
||||
}
|
||||
|
||||
template <std::size_t Count, std::size_t... Indices>
|
||||
inline std::size_t StringsInVectorImplem<Count, std::index_sequence<Indices...>>::raw_buffer_size() const {
|
||||
return m_memory.size();
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
using StringsInVector = StringsInVectorImplem<Count, decltype(std::make_index_sequence<Count>())>;
|
||||
|
||||
|
|
51
include/wrenpp/detail/wren_class_name_from_type.hpp
Normal file
51
include/wrenpp/detail/wren_class_name_from_type.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* Copyright 2020-2022, 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 "module_and_name.hpp"
|
||||
#include <utility>
|
||||
#include <string_view>
|
||||
#include <cstddef>
|
||||
|
||||
namespace wren {
|
||||
typedef std::size_t ClassManagerNameHashType;
|
||||
class VM;
|
||||
|
||||
namespace detail {
|
||||
//TODO: work in progress, make it take a type T and hash that for real
|
||||
template<typename T>
|
||||
inline ClassManagerNameHashType type_hash() {
|
||||
static const ClassManagerNameHashType my_hash = 0;
|
||||
return static_cast<ClassManagerNameHashType>(reinterpret_cast<std::uintptr_t>(&my_hash));
|
||||
}
|
||||
|
||||
//Just an utility function to call a method on the class manager in vm
|
||||
//and return its return value without including the header for the
|
||||
//class manager. Useful in inline functions in header files where
|
||||
//including class_manager.hpp would propagate it everywhere.
|
||||
ModuleAndName fetch_class_name_from_manager (
|
||||
const VM& vm,
|
||||
ClassManagerNameHashType hash
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
inline ModuleAndName wren_class_name_from_type (const VM& vm) {
|
||||
return fetch_class_name_from_manager(vm, type_hash<T>());
|
||||
}
|
||||
} //namespace detail
|
||||
} //namespace wren
|
|
@ -109,7 +109,9 @@ namespace wren {
|
|||
void reset (Configuration* conf);
|
||||
|
||||
CallbackManager& callback_manager();
|
||||
const CallbackManager& callback_manager() const;
|
||||
ClassManager& class_manager();
|
||||
const ClassManager& class_manager() const;
|
||||
|
||||
private:
|
||||
struct LocalData;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "detail/wren_types.hpp"
|
||||
#include "detail/construct_foreign_class.hpp"
|
||||
#include "detail/setters_getters.hpp"
|
||||
#include "detail/wren_class_name_from_type.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
@ -103,8 +104,15 @@ namespace wren {
|
|||
}
|
||||
else {
|
||||
auto ret = call_implem(std::make_integer_sequence<int, sizeof...(Args)>(), vm);
|
||||
//TODO: check for errors
|
||||
vm.ensure_slots(1);
|
||||
set(vm, 0, ret);
|
||||
if constexpr (std::is_fundamental<R>::value) {
|
||||
set(vm, 0, ret);
|
||||
}
|
||||
else {
|
||||
ModuleAndName wren_name = wren_class_name_from_type<R>(vm);
|
||||
R* const new_object = make_wren_object<R>(vm, wren_name, std::move(ret));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ wrenpp = library(meson.project_name(),
|
|||
'src/vm_fun.cpp',
|
||||
'src/callback_manager.cpp',
|
||||
'src/class_manager.cpp',
|
||||
'src/wren_class_name_from_type.cpp',
|
||||
dependencies: [wren_dep],
|
||||
include_directories: public_incl,
|
||||
install: (not meson.is_subproject() or get_option('default_library')=='shared'),
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "wrenpp/class_manager.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace wren {
|
||||
namespace detail {
|
||||
|
@ -36,6 +37,15 @@ namespace wren {
|
|||
{
|
||||
}
|
||||
|
||||
ClassNameOwning::operator ModuleAndName() const {
|
||||
return ModuleAndName{
|
||||
this->raw_buffer(),
|
||||
0, //TODO: replace with hash
|
||||
this->module_name().size(),
|
||||
this->class_name().size()
|
||||
};
|
||||
}
|
||||
|
||||
bool ClassNameOwning::operator== (const ClassNameOwning& other) const {
|
||||
return
|
||||
*static_cast<const ClassNameBase*>(this) == static_cast<const ClassNameBase&>(other) or
|
||||
|
@ -74,19 +84,6 @@ namespace wren {
|
|||
ClassManager::ClassManager() = default;
|
||||
ClassManager::~ClassManager() noexcept = default;
|
||||
|
||||
ClassManager& ClassManager::add_class_maker (std::string_view module_name, std::string_view class_name, make_foreign_class_t maker) {
|
||||
using detail::TempClassName;
|
||||
using detail::ClassNameOwning;
|
||||
|
||||
auto it_found = m_classes.find(TempClassName{module_name, class_name});
|
||||
if (m_classes.cend() == it_found)
|
||||
m_classes.insert(it_found, std::make_pair(ClassNameOwning{module_name, class_name}, maker));
|
||||
else
|
||||
it_found->second = maker;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
foreign_class_t ClassManager::make_class (std::string_view module_name, std::string_view class_name) {
|
||||
using detail::TempClassName;
|
||||
|
||||
|
@ -96,4 +93,47 @@ namespace wren {
|
|||
else
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
auto ClassManager::add_class_maker_implem (
|
||||
std::string_view module_name,
|
||||
std::string_view class_name,
|
||||
make_foreign_class_t maker
|
||||
) -> ClassMap::const_iterator {
|
||||
using detail::TempClassName;
|
||||
using detail::ClassNameOwning;
|
||||
|
||||
auto it_found = m_classes.find(TempClassName{module_name, class_name});
|
||||
ClassMap::const_iterator retval;
|
||||
if (m_classes.cend() == it_found) {
|
||||
retval = m_classes.insert(
|
||||
it_found,
|
||||
std::make_pair(ClassNameOwning{module_name, class_name}, maker)
|
||||
);
|
||||
}
|
||||
else {
|
||||
it_found->second = maker;
|
||||
retval = it_found;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ClassManager::add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name) {
|
||||
using detail::TempClassName;
|
||||
|
||||
assert(m_types.count(hash) == 0 or m_types.at(hash) == name);
|
||||
//insert if not present, leave old value if hash is already there
|
||||
m_types.insert(std::make_pair(hash, name));
|
||||
}
|
||||
|
||||
ModuleAndName ClassManager::wren_class_name_from_hash(ClassManagerNameHashType hash) const {
|
||||
const auto it_found = m_types.find(hash);
|
||||
if (m_types.cend() != it_found) {
|
||||
const detail::ClassNameOwning* const name = it_found->second;
|
||||
return *name;
|
||||
}
|
||||
else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} //namespace wren
|
||||
|
|
|
@ -289,6 +289,7 @@ namespace wren {
|
|||
}
|
||||
|
||||
bool VM::has_module(const char* module) const noexcept {
|
||||
assert(module);
|
||||
return wrenHasModule(m_local->wvm, module);
|
||||
}
|
||||
|
||||
|
@ -348,10 +349,18 @@ namespace wren {
|
|||
return m_local->callback_manager;
|
||||
}
|
||||
|
||||
const CallbackManager& VM::callback_manager() const {
|
||||
return m_local->callback_manager;
|
||||
}
|
||||
|
||||
ClassManager& VM::class_manager() {
|
||||
return m_local->class_manager;
|
||||
}
|
||||
|
||||
const ClassManager& VM::class_manager() const {
|
||||
return m_local->class_manager;
|
||||
}
|
||||
|
||||
void VM::ensure_slots (int num_slots) {
|
||||
wrenEnsureSlots(m_local->wvm, num_slots);
|
||||
}
|
||||
|
|
14
src/wren_class_name_from_type.cpp
Normal file
14
src/wren_class_name_from_type.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "wrenpp/detail/wren_class_name_from_type.hpp"
|
||||
#include "wrenpp/vm.hpp"
|
||||
#include "wrenpp/class_manager.hpp"
|
||||
|
||||
namespace wren {
|
||||
namespace detail {
|
||||
ModuleAndName fetch_class_name_from_manager (
|
||||
const VM& vm,
|
||||
ClassManagerNameHashType hash
|
||||
) {
|
||||
return vm.class_manager().wren_class_name_from_hash(hash);
|
||||
}
|
||||
} //namespace detail
|
||||
} //namespace wren
|
Loading…
Reference in a new issue