Use a better ID for identifying classes.

Upon testing (and reading), getting the address of a static
is safe even across .so boundaries. Moreover it doesn't incur
into the risk of collisions, unlike hashes, and also works
with different classes with the same name (for example from
two different unnamed namespaces). At this point the crc32
stuff is pretty much redundant, I'll try to remove it next.
This commit is contained in:
King_DuckZ 2022-06-03 09:39:43 +02:00
parent 616381516b
commit 09530e15c9
10 changed files with 164 additions and 70 deletions

View file

@ -21,6 +21,7 @@
#include "detail/strings_in_vector.hpp" #include "detail/strings_in_vector.hpp"
#include "detail/construct_foreign_class.hpp" #include "detail/construct_foreign_class.hpp"
#include "detail/wren_class_name_from_type.hpp" #include "detail/wren_class_name_from_type.hpp"
#include "detail/type_id.hpp"
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@ -79,22 +80,28 @@ namespace wren {
std::size_t operator()(TempClassName value) const; std::size_t operator()(TempClassName value) const;
}; };
template <typename F> class TypeIDHash {
struct CppClassTypeHelper { public:
static void register_ifp(ClassManager&, const ClassNameOwning* name); std::size_t operator()(TypeID tid) const;
};
define_has_typedef(class_type, ClassType);
} //namespace detail
//Customisation point - specialise for your own types if needed
template <typename T>
struct ClassMakerTraits {
}; };
template <typename T, typename... Args> template <typename T, typename... Args>
struct CppClassTypeHelper<MakeForeignClass<T, Args...>> { struct ClassMakerTraits<detail::MakeForeignClass<T, Args...>> {
static void register_ifp(ClassManager& classman, const ClassNameOwning* name); typedef T class_type;
}; };
} //namespace detail
class ClassManager { class ClassManager {
template <typename F> friend struct detail::CppClassTypeHelper;
typedef std::function<foreign_class_t()> make_foreign_class_t; 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<detail::ClassNameOwning, make_foreign_class_t, detail::ClassNameHash, detail::ClassNameEqual> ClassMap;
typedef std::unordered_map<ClassManagerNameHashType, const detail::ClassNameOwning*> TypeMap; typedef std::unordered_map<TypeID, const detail::ClassNameOwning*, detail::TypeIDHash> TypeMap;
public: public:
ClassManager(); ClassManager();
~ClassManager() noexcept; ~ClassManager() noexcept;
@ -102,7 +109,7 @@ namespace wren {
template <typename F> template <typename F>
ClassManager& add_class_maker (std::string module_name, std::string_view class_name, F&& maker); 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); foreign_class_t make_class(std::string_view module_name, std::string_view class_name);
ModuleAndName wren_class_name_from_hash(ClassManagerNameHashType hash) const; ModuleAndName wren_class_name_from_hash(TypeID hash) const;
template <typename T> template <typename T>
ModuleAndName wren_class_name_from_type() const; ModuleAndName wren_class_name_from_type() const;
@ -113,33 +120,27 @@ namespace wren {
make_foreign_class_t make_foreign_class_t
); );
void add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name); void add_type (TypeID hash, const detail::ClassNameOwning* name);
ClassMap m_classes; ClassMap m_classes;
TypeMap m_types; //refers to memory in 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> template <typename F>
inline ClassManager& ClassManager::add_class_maker (std::string module_name, std::string_view class_name, F&& maker) { inline ClassManager& ClassManager::add_class_maker (std::string module_name, std::string_view class_name, F&& maker) {
typedef ClassMakerTraits<std::decay_t<F>> MakerTraits;
auto it_new = this->add_class_maker_implem(module_name, class_name, std::forward<F>(maker)); auto it_new = this->add_class_maker_implem(module_name, class_name, std::forward<F>(maker));
//See if we can deduce the class type that maker is supposed to work
//with. This is not guaranteed to be an existing piece of info because
//user code can provide their own function that doesn't use any actual
//c++ class, just normal functions.
if constexpr (detail::HasClassTypeTypedef<MakerTraits>::value) {
const auto& owning_name = it_new->first; const auto& owning_name = it_new->first;
detail::CppClassTypeHelper<std::decay_t<F>>::register_ifp(*this, &owning_name); this->add_type(type_id<typename MakerTraits::class_type>(), &owning_name);
}
return *this; return *this;
} }

View file

@ -66,5 +66,17 @@
}; \ }; \
} }
#define define_has_typedef(typedef_name,pretty_name) \
template <typename T> \
struct Has ## pretty_name ## Typedef { \
private: \
struct TrueType { int a[2]; }; \
typedef int FalseType; \
template <typename C> static TrueType has_typedef ( const typename C::typedef_name* ); \
template <typename C> static FalseType has_typedef ( ... ); \
public: \
enum { value = sizeof(has_typedef<T>(nullptr)) == sizeof(TrueType) }; \
}
namespace wren { namespace wren {
} //namespace wren } //namespace wren

View file

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

View file

@ -25,6 +25,7 @@
#include <type_traits> #include <type_traits>
namespace wren { namespace wren {
typedef decltype(crc32c(nullptr, 0)) ModuleAndNameHashType;
class ModuleAndName; class ModuleAndName;
namespace detail { namespace detail {
@ -37,8 +38,10 @@ namespace wren {
bool deep_equal (const ModuleAndName& a, const ModuleAndName& b) noexcept; bool deep_equal (const ModuleAndName& a, const ModuleAndName& b) noexcept;
} //unnamed namespace } //unnamed namespace
//Non-owning, lightweight class for storing a module nome/class name pair. //Non-owning, lightweight class for storing a module name + generic name
//They are stored as consecutive strings within a single buffer with a //pair. It holds a hash value too, which is currently only meaningful for
//module name/class name pairs that are mapped to a c++ class.
//Names are stored as consecutive strings within a single buffer with a
//null-char in-between and at the end so that each name is null-terminated //null-char in-between and at the end so that each name is null-terminated
//and can be used with C APIs. //and can be used with C APIs.
//Keep the size small, ideally within 2 registers size. Altering this might //Keep the size small, ideally within 2 registers size. Altering this might
@ -51,7 +54,7 @@ namespace wren {
friend bool detail::deep_equal(const ModuleAndName&, const ModuleAndName&) noexcept; friend bool detail::deep_equal(const ModuleAndName&, const ModuleAndName&) noexcept;
public: public:
constexpr ModuleAndName(); constexpr ModuleAndName();
constexpr ModuleAndName (const char* base, std::uint32_t hash, std::size_t mod_name_len, std::size_t class_name_len); constexpr ModuleAndName (const char* base, ModuleAndNameHashType hash, std::size_t mod_name_len, std::size_t class_name_len);
constexpr bool operator==(const ModuleAndName& other) const noexcept; constexpr bool operator==(const ModuleAndName& other) const noexcept;
constexpr bool operator!=(const ModuleAndName& other) const noexcept; constexpr bool operator!=(const ModuleAndName& other) const noexcept;
@ -64,14 +67,20 @@ namespace wren {
constexpr std::string_view module_name() const noexcept { return {module_name_char(), m_mod_name_len}; } constexpr std::string_view module_name() const noexcept { return {module_name_char(), m_mod_name_len}; }
constexpr std::string_view class_name() const noexcept { return {class_name_char(), m_class_name_len}; } constexpr std::string_view class_name() const noexcept { return {class_name_char(), m_class_name_len}; }
constexpr const char* buffer_address() const noexcept { return m_base; } constexpr const char* buffer_address() const noexcept { return m_base; }
constexpr ModuleAndNameHashType hash() const noexcept { return m_hash; }
private: private:
constexpr std::size_t raw_buffer_size() const noexcept; constexpr std::size_t raw_buffer_size() const noexcept;
const char* m_base; const char* m_base;
std::uint32_t m_hash; ModuleAndNameHashType m_hash;
std::uint16_t m_mod_name_len; std::uint16_t m_mod_name_len;
std::uint16_t m_class_name_len; std::uint16_t m_class_name_len;
static_assert(
sizeof(m_hash) <= sizeof(std::uint32_t),
"Hash type is too large, review this struct or change it back to uint32_t"
);
}; };
union RawAccessModuleAndName { union RawAccessModuleAndName {
@ -93,7 +102,7 @@ namespace wren {
ModuleAndName(nullptr, 0, 0, 0) 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) : constexpr ModuleAndName::ModuleAndName (const char* base, ModuleAndNameHashType hash, std::size_t mod_name_len, std::size_t class_name_len) :
m_base(base), m_base(base),
m_hash(hash), m_hash(hash),
m_mod_name_len(mod_name_len), m_mod_name_len(mod_name_len),
@ -137,7 +146,10 @@ namespace wren {
constexpr const char* data = StaticStorage::value.data(); constexpr const char* data = StaticStorage::value.data();
constexpr std::uint16_t s1_len = static_cast<std::uint16_t>(S1.size()); constexpr std::uint16_t s1_len = static_cast<std::uint16_t>(S1.size());
constexpr std::uint16_t s2_len = static_cast<std::uint16_t>(S2.size()); constexpr std::uint16_t s2_len = static_cast<std::uint16_t>(S2.size());
constexpr std::uint32_t hash = crc32c(data, StaticStorage::value.size()); constexpr ModuleAndNameHashType hash = crc32c(
StaticStorage::value.data(),
StaticStorage::value.size()
);
return ModuleAndName{data, hash, s1_len, s2_len}; return ModuleAndName{data, hash, s1_len, s2_len};
} }

View file

@ -0,0 +1,71 @@
/* 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 <cstdint>
namespace wren {
//template <typename U>
//[[gnu::const]]
//inline constexpr std::uint32_t type_hash() {
// return crc32c(__PRETTY_FUNCTION__);
//}
//from:
//https://codereview.stackexchange.com/questions/48594/unique-type-id-no-rtti
class TypeID {
using sig = TypeID();
sig* m_id;
constexpr TypeID(sig* id) : m_id{id} {}
public:
template<typename T>
friend constexpr TypeID type_id();
TypeID (const TypeID&) = default;
TypeID (TypeID&&) = default;
~TypeID() noexcept = default;
TypeID& operator= (const TypeID&) = default;
TypeID& operator= (TypeID&&) = default;
constexpr bool operator==(TypeID other) const noexcept;
constexpr bool operator!=(TypeID other) const noexcept;
constexpr bool operator<(TypeID other) const noexcept;
constexpr std::uintptr_t id() const noexcept;
};
template<typename T>
constexpr TypeID type_id() { return &type_id<T>; }
constexpr bool TypeID::operator==(TypeID other) const noexcept {
return id() == other.id();
}
constexpr bool TypeID::operator!=(TypeID other) const noexcept {
return id() != other.id();
}
constexpr bool TypeID::operator<(TypeID other) const noexcept {
return id() < other.id();
}
constexpr std::uintptr_t TypeID::id() const noexcept {
return reinterpret_cast<std::uintptr_t>(m_id);
}
} //namespace wren

View file

@ -18,34 +18,27 @@
#pragma once #pragma once
#include "module_and_name.hpp" #include "module_and_name.hpp"
#include "type_id.hpp"
#include "crc32.hpp"
#include <utility> #include <utility>
#include <string_view> #include <string_view>
#include <cstddef>
namespace wren { namespace wren {
typedef std::size_t ClassManagerNameHashType;
class VM; class VM;
namespace detail { 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 //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 //and return its return value without including the header for the
//class manager. Useful in inline functions in header files where //class manager. Useful in inline functions in header files where
//including class_manager.hpp would propagate it everywhere. //including class_manager.hpp would propagate it everywhere.
ModuleAndName fetch_class_name_from_manager ( ModuleAndName fetch_class_name_from_manager (
const VM& vm, const VM& vm,
ClassManagerNameHashType hash TypeID hash
); );
template <typename T> template <typename T>
inline ModuleAndName wren_class_name_from_type (const VM& vm) { inline ModuleAndName wren_class_name_from_type (const VM& vm) {
return fetch_class_name_from_manager(vm, type_hash<T>()); return fetch_class_name_from_manager(vm, type_id<T>());
} }
} //namespace detail } //namespace detail
} //namespace wren } //namespace wren

View file

@ -19,7 +19,7 @@
#include "detail/has_method.hpp" #include "detail/has_method.hpp"
#include "detail/error_type.hpp" #include "detail/error_type.hpp"
#include "detail/crc32.hpp" #include "detail/type_id.hpp"
#include "detail/wren_types.hpp" #include "detail/wren_types.hpp"
#include "handle.hpp" #include "handle.hpp"
#include <memory> #include <memory>
@ -116,11 +116,11 @@ namespace wren {
private: private:
struct LocalData; struct LocalData;
VM (Configuration* conf, const detail::Callbacks&, void* user_data, std::uint32_t user_data_type); VM (Configuration* conf, const detail::Callbacks&, void* user_data, TypeID user_data_type);
DynafuncMaker* dynafunc_maker(); DynafuncMaker* dynafunc_maker();
std::uint32_t user_data_type() const; TypeID user_data_type() const;
void* void_user_data(); void* void_user_data();
void set_user_data (void* user_data, std::uint32_t user_data_type); void set_user_data (void* user_data, TypeID user_data_type);
template <typename T> detail::Callbacks to_callbacks (T& conf); template <typename T> detail::Callbacks to_callbacks (T& conf);
std::unique_ptr<LocalData> m_local; std::unique_ptr<LocalData> m_local;
@ -151,12 +151,6 @@ namespace wren {
return (*M)(args...); return (*M)(args...);
} }
}; };
template <typename U>
[[gnu::const]]
inline consteval std::uint32_t type_id() {
return crc32c(__PRETTY_FUNCTION__);
}
} //namespace detail } //namespace detail
template <typename T> template <typename T>
@ -198,7 +192,7 @@ namespace wren {
template <typename U> template <typename U>
inline U* VM::user_data() { inline U* VM::user_data() {
if (user_data_type() != detail::type_id<U>()) { if (user_data_type() != type_id<U>()) {
throw std::runtime_error("Wrong user data type requested"); throw std::runtime_error("Wrong user data type requested");
} }
return reinterpret_cast<U*>(void_user_data()); return reinterpret_cast<U*>(void_user_data());
@ -206,7 +200,7 @@ namespace wren {
template <typename U> template <typename U>
inline void VM::set_user_data (U* user_data) { inline void VM::set_user_data (U* user_data) {
set_user_data(reinterpret_cast<void*>(user_data), detail::type_id<U>()); set_user_data(reinterpret_cast<void*>(user_data), type_id<U>());
} }
template <typename T> template <typename T>
@ -215,7 +209,7 @@ namespace wren {
static_cast<Configuration*>(conf), static_cast<Configuration*>(conf),
to_callbacks(*conf), to_callbacks(*conf),
nullptr, nullptr,
detail::type_id<std::nullptr_t>() type_id<std::nullptr_t>()
) )
{ {
} }
@ -226,7 +220,7 @@ namespace wren {
static_cast<Configuration*>(conf), static_cast<Configuration*>(conf),
to_callbacks(*conf), to_callbacks(*conf),
reinterpret_cast<void*>(user_data), reinterpret_cast<void*>(user_data),
detail::type_id<U>() type_id<U>()
) )
{ {
} }

View file

@ -80,6 +80,15 @@ namespace wren {
std::size_t ClassNameHash::operator()(TempClassName value) const { std::size_t ClassNameHash::operator()(TempClassName value) const {
return value.module_hash() ^ (value.class_hash() << 1); return value.module_hash() ^ (value.class_hash() << 1);
} }
std::size_t TypeIDHash::operator()(TypeID tid) const {
if constexpr (sizeof(tid.id()) > sizeof(std::size_t)) {
return std::hash<decltype(tid.id())>{}(tid.id());
}
else {
return static_cast<std::size_t>(tid.id());
}
}
} //namespace detail } //namespace detail
ClassManager::ClassManager() = default; ClassManager::ClassManager() = default;
@ -119,15 +128,15 @@ namespace wren {
return retval; return retval;
} }
void ClassManager::add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name) { void ClassManager::add_type (TypeID hash, const detail::ClassNameOwning* name) {
using detail::TempClassName; using detail::TempClassName;
assert(m_types.count(hash) == 0 or m_types.at(hash) == name); assert(m_types.count(hash) == 0 or *m_types.at(hash) == *name);
//insert if not present, leave old value if hash is already there //insert if not present, leave old value if hash is already there
m_types.insert(std::make_pair(hash, name)); m_types.insert(std::make_pair(hash, name));
} }
ModuleAndName ClassManager::wren_class_name_from_hash(ClassManagerNameHashType hash) const { ModuleAndName ClassManager::wren_class_name_from_hash(TypeID hash) const {
const auto it_found = m_types.find(hash); const auto it_found = m_types.find(hash);
if (m_types.cend() != it_found) { if (m_types.cend() != it_found) {
const detail::ClassNameOwning* const name = it_found->second; const detail::ClassNameOwning* const name = it_found->second;

View file

@ -162,7 +162,8 @@ namespace wren {
struct VM::LocalData { struct VM::LocalData {
explicit LocalData (const detail::Callbacks& cb) : explicit LocalData (const detail::Callbacks& cb) :
callbacks(cb), callbacks(cb),
wvm(nullptr) wvm(nullptr),
user_data_type(type_id<std::nullptr_t>())
{ {
callbacks.dynafunc = &dynafunc; callbacks.dynafunc = &dynafunc;
} }
@ -180,10 +181,10 @@ namespace wren {
DynafuncMaker dynafunc; DynafuncMaker dynafunc;
WrenVM* wvm; WrenVM* wvm;
void* user_data; void* user_data;
std::uint32_t user_data_type; TypeID user_data_type;
}; };
VM::VM (Configuration* conf, const detail::Callbacks& cb, void* user_data, std::uint32_t user_data_type) : VM::VM (Configuration* conf, const detail::Callbacks& cb, void* user_data, TypeID user_data_type) :
m_local(std::make_unique<LocalData>(cb)) m_local(std::make_unique<LocalData>(cb))
{ {
this->reset(conf); this->reset(conf);
@ -197,7 +198,7 @@ namespace wren {
return &m_local->dynafunc; return &m_local->dynafunc;
} }
std::uint32_t VM::user_data_type() const { TypeID VM::user_data_type() const {
return m_local->user_data_type; return m_local->user_data_type;
} }
@ -205,7 +206,7 @@ namespace wren {
return m_local->user_data; return m_local->user_data;
} }
void VM::set_user_data (void* user_data, std::uint32_t user_data_type) { void VM::set_user_data (void* user_data, TypeID user_data_type) {
m_local->user_data = user_data; m_local->user_data = user_data;
m_local->user_data_type = user_data_type; m_local->user_data_type = user_data_type;
} }
@ -281,7 +282,7 @@ namespace wren {
void VM::set_user_data (std::nullptr_t) { void VM::set_user_data (std::nullptr_t) {
m_local->user_data = nullptr; m_local->user_data = nullptr;
m_local->user_data_type = detail::type_id<std::nullptr_t>(); m_local->user_data_type = type_id<std::nullptr_t>();
} }
bool VM::has_user_data() const noexcept { bool VM::has_user_data() const noexcept {

View file

@ -6,7 +6,7 @@ namespace wren {
namespace detail { namespace detail {
ModuleAndName fetch_class_name_from_manager ( ModuleAndName fetch_class_name_from_manager (
const VM& vm, const VM& vm,
ClassManagerNameHashType hash TypeID hash
) { ) {
return vm.class_manager().wren_class_name_from_hash(hash); return vm.class_manager().wren_class_name_from_hash(hash);
} }