169 lines
5.6 KiB
C++
169 lines
5.6 KiB
C++
/* Copyright 2020-2024, 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 "setters_getters.hpp"
|
|
#if defined(WRENPP_WITH_NAME_GUESSING)
|
|
# include "guess_class_name.hpp"
|
|
#endif
|
|
#include <utility>
|
|
#include <tuple>
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
|
|
namespace wren {
|
|
namespace detail {
|
|
template <typename T, typename... Args, int... Indices>
|
|
inline void construct_single (
|
|
std::integer_sequence<int, Indices...>,
|
|
VM& vm,
|
|
void* memory
|
|
) {
|
|
new(memory) T{get<Args>(vm, Indices + 1)...};
|
|
}
|
|
|
|
struct ConstructResult {
|
|
std::uint16_t parameter_count;
|
|
bool success;
|
|
};
|
|
|
|
template <typename T, typename... Args>
|
|
struct ForeignClassHelper;
|
|
|
|
template <typename T, template <typename...> class Pack, typename... PackArgs, typename... Args>
|
|
struct ForeignClassHelper<T, Pack<PackArgs...>, Args...> {
|
|
static ConstructResult construct (VM& vm, int cardinality, void* memory) {
|
|
using std::make_integer_sequence;
|
|
|
|
//search by number of parameters.
|
|
//once we find a tuple-like parameter that contains the right
|
|
//number of types we invoke construct_single() with those types.
|
|
constexpr int pack_size = static_cast<int>(sizeof...(PackArgs));
|
|
if (pack_size == cardinality) {
|
|
construct_single<T, PackArgs...>(make_integer_sequence<int, pack_size>{}, vm, memory);
|
|
return ConstructResult{pack_size, true};
|
|
}
|
|
else {
|
|
return ForeignClassHelper<T, Args...>::construct(vm, cardinality, memory);
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename T, typename LoneArg, typename... Args>
|
|
struct ForeignClassHelper<T, LoneArg, Args...> {
|
|
static ConstructResult construct (VM& vm, int cardinality, void* memory) {
|
|
using std::make_integer_sequence;
|
|
|
|
if (1 == cardinality) {
|
|
construct_single<T, LoneArg>(make_integer_sequence<int, 1>{}, vm, memory);
|
|
return ConstructResult{1, true};
|
|
}
|
|
else {
|
|
return ForeignClassHelper<T, Args...>::construct(vm, cardinality, memory);
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ForeignParamHelper {
|
|
static constexpr std::size_t argument_count = 1;
|
|
};
|
|
|
|
template <template <typename...> class Pack, typename... PackArgs>
|
|
struct ForeignParamHelper<Pack<PackArgs...>> {
|
|
static constexpr std::size_t argument_count = sizeof...(PackArgs);
|
|
};
|
|
|
|
template <typename T>
|
|
struct ForeignClassHelper<T> {
|
|
static ConstructResult construct (VM& vm, int cardinality, void* memory) {
|
|
using std::make_integer_sequence;
|
|
|
|
if (cardinality == 0) {
|
|
construct_single<T>(make_integer_sequence<int, 0>{}, vm, memory);
|
|
return ConstructResult{0, true};
|
|
}
|
|
else {
|
|
return ConstructResult{static_cast<std::uint16_t>(cardinality), false};
|
|
}
|
|
}
|
|
};
|
|
|
|
template <std::size_t Search, std::size_t... Values>
|
|
struct AssertIfDuplicateValues {
|
|
static_assert(sizeof...(Values) == 0, "Code bug?");
|
|
};
|
|
|
|
template <std::size_t Search, std::size_t Value, std::size_t... Values>
|
|
struct AssertIfDuplicateValues<Search, Value, Values...> : AssertIfDuplicateValues<Search, Values...>, AssertIfDuplicateValues<Value, Values...> {
|
|
static_assert(Search != Value, "Constructor overloads with the same argument counts are not currently allowed");
|
|
static constexpr std::size_t value = 1;
|
|
};
|
|
|
|
template <typename... Args>
|
|
constexpr void static_assert_all_packs_are_unique() {
|
|
//If more than one arg (ctor overload) is given, check they are unique
|
|
if constexpr (sizeof...(Args) > 1) {
|
|
constexpr auto dummy = AssertIfDuplicateValues<ForeignParamHelper<Args>::argument_count...>::value;
|
|
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) {
|
|
abort_fiber_with_error(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")
|
|
);
|
|
}
|
|
};
|
|
ret.finalize = [](void* mem) {
|
|
const auto cale = static_cast<T*>(mem);
|
|
cale->~T();
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
};
|
|
} //namespace detail
|
|
|
|
template <typename T, typename... Args>
|
|
constexpr detail::MakeForeignClass<T, Args...> make_foreign_class;
|
|
} //namespace wren
|