Add support for registering foreign methods that return foreign objects

Still a bit work in progress but functioning
This commit is contained in:
King_DuckZ 2022-05-18 18:48:18 +02:00
parent 9c147b1a6e
commit 1fa6d62f17
15 changed files with 264 additions and 45 deletions

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -8,6 +8,7 @@ include_files = [
'string_bt.hpp',
'StringCRC32.hpp',
'strings_in_vector.hpp',
'wren_class_name_from_type.hpp',
'wren_types.hpp',
]

View file

@ -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),

View file

@ -17,7 +17,7 @@
#pragma once
#include "wrenpp/vm.hpp"
#include "../vm.hpp"
#include <string_view>
#include <vector>
#include <tuple>

View file

@ -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>())>;

View 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

View file

@ -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;

View file

@ -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));
}
}
};
}

View file

@ -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'),

View file

@ -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

View file

@ -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);
}

View 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