Skip to content

Commit

Permalink
Merge pull request #85 from iboB/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
iboB committed Apr 20, 2023
2 parents 04baf4f + 0578964 commit 1da1554
Show file tree
Hide file tree
Showing 22 changed files with 292 additions and 62 deletions.
1 change: 1 addition & 0 deletions code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ target_sources(dynamix PRIVATE
dynamix/bits/make_from_tuple.hpp
dynamix/bits/make_nullptr.hpp
dynamix/bits/q_const.hpp
dynamix/bits/type_name_from_typeid.hpp

dynamix/compat/pmr/pmr.hpp
dynamix/compat/pmr/allocator.hpp
Expand Down
2 changes: 1 addition & 1 deletion code/dnmx/mixin_info_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@
(mi).copy_init = dnmx_mixin_noop_copy_func; \
(mi).copy_asgn = dnmx_mixin_noop_copy_func; \
(mi).compare = dnmx_mixin_noop_cmp_func; \
} while (false)
} while (false)
43 changes: 43 additions & 0 deletions code/dynamix/bits/type_name_from_typeid.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Borislav Stanimirov
// SPDX-License-Identifier: MIT
//
#pragma once
#include <typeinfo>
#include <string>
#include <itlib/strutil.hpp>

#if defined(__GNUC__)
# include <cstdlib>
# include <cxxabi.h>
#endif

namespace dynamix::util {
inline std::string get_type_name_from_typeid(const char* typeid_name) {
#if defined(__GNUC__) // __GNUC__ is defined with clang
// use cxxabi to unmangle the gcc typeid name
int cxa_demangle_status = 0;
char* demangled = abi::__cxa_demangle(typeid_name, nullptr, nullptr, &cxa_demangle_status);
std::string ret = demangled;
free(demangled);
return ret;
#elif defined(_MSC_VER)
std::string_view sv_name = typeid_name;

// msvc typeid names are "class x" or "struct x" instead of "x",
// so we just offset the pointer appropriately
// C++20 use std::string_view::starts_with
if (itlib::starts_with(sv_name, "enum ")) return typeid_name + 5;
if (itlib::starts_with(sv_name, "class ")) return typeid_name + 6;
if (itlib::starts_with(sv_name, "struct ")) return typeid_name + 7;

return typeid_name;
#else
# error "getting typenames with typeid hasn't been tested on compilers other than gcc, clang, and msvc"
#endif
}

template <typename T>
std::string get_type_name_from_typeid() {
return get_type_name_from_typeid(typeid(T).name());
}
}
2 changes: 1 addition & 1 deletion code/dynamix/declare_mixin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include "mixin_info_fwd.hpp"

#define DYNAMIX_DECLARE_EXPORTED_MIXIN(export, mixin) \
extern export const ::dynamix::mixin_info& _dynamix_get_mixin_info(mixin* m)
extern export const ::dynamix::mixin_info& _dynamix_get_mixin_info(const mixin*)

#define DYNAMIX_DECLARE_MIXIN(mixin) \
DYNAMIX_DECLARE_EXPORTED_MIXIN(I_DNMX_PP_EMPTY(), mixin)
2 changes: 1 addition & 1 deletion code/dynamix/define_mixin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct mixin_info_data_instance {

#define DYNAMIX_DEFINE_MIXIN_EX(domain_tag, mixin, builder) \
static const ::dynamix::util::mixin_info_data& I_DYNAMIX_MIXIN_INFO_VAR(mixin) = ::dynamix::impl::mixin_info_data_instance<domain_tag, mixin>::the_data_safe(); \
const ::dynamix::mixin_info& _dynamix_get_mixin_info(mixin*) { \
const ::dynamix::mixin_info& _dynamix_get_mixin_info(const mixin*) { \
return I_DYNAMIX_MIXIN_INFO_VAR(mixin).info; \
} \
using namespace ::dynamix::util::builder_literals; \
Expand Down
5 changes: 5 additions & 0 deletions code/dynamix/mutate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,14 @@ class gradual_mutation {
}

void apply() {
m_applied = true;
auto& type = m_obj.get_domain().get_type(std::move(m_type_mut));
m_obj.reset_type(type);
}

void cancel() {
m_applied = true;
}
};
inline gradual_mutation mutate(object& obj) {
return gradual_mutation(obj);
Expand Down
2 changes: 2 additions & 0 deletions code/dynamix/mutate_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ inline void mutate_to(object& obj, const type& type) {

template <typename... Ops>
void mutate_to(object& obj, const type& type, Ops&&... ops) {
if (obj.get_type() == type) return; // noop

// here we need to:
// * filter out ops that are not applicable for the object mutation which is to come
// * sort the applicable ones by index in the new type
Expand Down
6 changes: 5 additions & 1 deletion code/dynamix/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,12 @@ void object::clear() {
}

void object::reset_type(const type& type) {
if (m_sealed) throw_exception::obj_mut_sealed_object(get_type(), "reset_type");
if (&type == m_type) return; // noop

// optimization: check if type has zero mixins instead of calling domain::get_empty_type
if (type.num_mixins() == 0) {
if (m_sealed) throw_exception::obj_mut_sealed_object(get_type(), "reset_type");
assert(type == type.dom.get_empty_type()); // it must be the empty type, though
clear_mixin_data();
m_type = &type;
return;
Expand Down
33 changes: 27 additions & 6 deletions code/dynamix/object_mutation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,23 @@ void object_mutation::deallocate_external_mixin(void* ptr, const mixin_info& inf
);
}

// * allocated using own allocator
// * fill object pointer appropriately to newly allocated mixins
// * attach common external mixins to mixin_data elements
// * no mixin initialization here
void object_mutation::allocate_mixin_data() {
assert(m_allocated_upto == 0);

if (m_target_type == *m_old_type) {
// tag: same_type_shortcut
// we've somehow ended-up mutating the object with the same type
// in this case we know that there are no new mixins and only common ones
// we can skip allocating new mixin data altogether
m_target_mixin_data = m_old_mixin_data;
return;
}

// * allocate using object allocator
// * fill object pointer appropriately to newly allocated mixins
// * attach common external mixins to mixin_data elements
// * no mixin initialization here

auto buf = m_target_type.allocate_object_buffer(m_object.m_allocator);
m_target_mixin_data = reinterpret_cast<object_mixin_data*>(buf);

Expand Down Expand Up @@ -171,14 +181,25 @@ void object_mutation::destroy_new_mixins() noexcept {

void object_mutation::rollback() noexcept {
m_complete = false;
destroy_new_mixins();
deallocate_mixin_data();
if (m_target_type != *m_old_type) {
// tag: same_type_shortcut
// no new mixins, no allocated mixin data
destroy_new_mixins();
deallocate_mixin_data();
}
m_moved_out_from = true;
}

void object_mutation::finalize() noexcept {
if (!m_complete) return;

if (m_target_type == *m_old_type) {
// tag: same_type_shortcut
m_old_mixin_data = nullptr;
m_old_type = nullptr;
return;
}

// free old data if any
if (m_old_mixin_data) {
assert(m_old_type);
Expand Down
2 changes: 1 addition & 1 deletion code/dynamix/throw_exception.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace throw_exception {
// type_error
[[noreturn]] void mutation_rule_user_error(const type_mutation& mut, const mutation_rule_info& info, error_return_t error);
[[noreturn]] void cyclic_rule_deps(const type_mutation& mut);
[[noreturn]] void type_mut_error(const type_mutation& mut, std::string_view err, const mixin_info& m);
[[noreturn]] DYNAMIX_API void type_mut_error(const type_mutation& mut, std::string_view err, const mixin_info& m);
[[noreturn]] void type_mut_error(const type_mutation& mut, std::string_view op, std::string_view mixin_name);
[[noreturn]] void feature_clash(const type_mutation& mut, const dnmx_ftable_payload& a, const dnmx_ftable_payload& b);
[[noreturn]] void unknown_type_class(const type& t, const std::string_view name);
Expand Down
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ dynamix_test(msg t-msg.cpp)

dynamix_test(danger t-danger.cpp)

dynamix_test(bits-type_name_from_typeid t-bits-type_name_from_typeid.cpp)

if(DYNAMIX_SHARED)
add_subdirectory(app-plugin)
endif()
Expand Down
28 changes: 28 additions & 0 deletions test/t-bits-type_name_from_typeid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Borislav Stanimirov
// SPDX-License-Identifier: MIT
//
#include <dynamix/bits/type_name_from_typeid.hpp>

#include <doctest/doctest.h>

struct sfoo {};
class cbar {};

namespace test {
struct a_struct {};
namespace inner {
class some_class {};
}
enum myenum { a, b, c };
}

TEST_CASE("type_name_from_typeid") {
using dynamix::util::get_type_name_from_typeid;

CHECK(get_type_name_from_typeid<sfoo>() == "sfoo");
CHECK(get_type_name_from_typeid<cbar>() == "cbar");
CHECK(get_type_name_from_typeid<unsigned int>() == "unsigned int");
CHECK(get_type_name_from_typeid<test::a_struct>() == "test::a_struct");
CHECK(get_type_name_from_typeid<test::inner::some_class>() == "test::inner::some_class");
CHECK(get_type_name_from_typeid<test::myenum>() == "test::myenum");
}
5 changes: 5 additions & 0 deletions test/t-define_mixin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ TEST_CASE("declared mixins only") {
CHECK(obj2.empty());
CHECK(obj3.get<bob>() == o2bob);

auto co2bob = obj3.get<const bob>();
CHECK(co2bob);
static_assert(std::is_same_v<decltype(co2bob), const bob*>);
CHECK(co2bob == o2bob);

{
const auto& cobj3 = obj3;
CHECK(cobj3.has<bob>());
Expand Down
24 changes: 22 additions & 2 deletions test/t-object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ TEST_CASE("seal") {
CHECK_THROWS_WITH_AS(mutate(sobj, add(*t.mesh)), "sl: mutate sealed object of type {'movable'}", object_error);
CHECK(sobj.equals(scopy));

CHECK_THROWS_WITH_AS(sobj.reset_type(*t.t_mov), "sl: mutate sealed object of type {'movable'}", object_error);
CHECK_THROWS_WITH_AS(sobj.reset_type(*t.t_afmi), "sl: reset_type sealed object of type {'movable'}", object_error);
CHECK(sobj.equals(scopy));

{
Expand All @@ -873,4 +873,24 @@ TEST_CASE("seal") {

object csobj(*t.t_asim, {}, true);
CHECK(csobj.sealed());
}
}

TEST_CASE("same type") {
test_data t;
domain dom("sl");
t.register_all_mixins(dom);
t.create_types(dom);

object obj(*t.t_afmi);
const auto inv = obj.get(*t.invisible);
CHECK(inv);

obj.reset_type(*t.t_afmi);
CHECK(inv == obj.get(*t.invisible));

mutate_to(obj, *t.t_afmi);
CHECK(inv == obj.get(*t.invisible));

perform_object_mutation(obj, *t.t_afmi);
CHECK(inv == obj.get(*t.invisible));
}
2 changes: 2 additions & 0 deletions test/v1compat/tv1-core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ TEST_CASE("complex_apply_mutation") {
CHECK_FALSE(o.get<counter>());
CHECK(!o.implements(dummy_msg));
CHECK(!o.implements(unused_msg));
CHECK(!o.get_type().implements_strong("dummy"));

mutate(o).add<no_messages>();
CHECK(o.has<no_messages>());
Expand All @@ -142,6 +143,7 @@ TEST_CASE("complex_apply_mutation") {
CHECK(o.has<counter>());
CHECK(o.get<counter>());
CHECK(o.implements(dummy_msg));
CHECK(o.get_type().implements_strong("dummy"));
CHECK(o.get<counter>()->get_count() == 0);
o.get<counter>()->count_uni();
CHECK(o.get<counter>()->get_count() == 1);
Expand Down

0 comments on commit 1da1554

Please sign in to comment.