Skip to content

Commit

Permalink
Copy lvalues instead of referencing them
Browse files Browse the repository at this point in the history
signal::connect has been reworked. It now does one of the following 3:
* Stores a function pointer if the callable can be converted to one
* Constructs the callable in the pointer's memory via perfect forwarding
* Constructs the callable on the heap via perfect forwarding

One relevant test in main.cpp has been updated, and a memory leak in another
test was fixed.

Fixes TheWisp#20
  • Loading branch information
mikezackles committed Jan 16, 2021
1 parent 7ff59d1 commit 072b48b
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 42 deletions.
10 changes: 6 additions & 4 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ namespace FunctorTest
{
struct Functor
{
int val = 0;
int &val;
void operator()(int x)
{
val += x;
Expand All @@ -201,11 +201,12 @@ namespace FunctorTest

void test()
{
Functor o;
int val = 0;
Functor o{val};
signal<void(int)> sig;
sig.connect(o);
sig(10);
assert(o.val == 10);
assert(val == 10);
}
}

Expand Down Expand Up @@ -248,6 +249,7 @@ void test()
foo.sig(1.f);
assert(bar1.total == 2);
assert(bar3->total == 2);
delete bar3;
}
foo.sig(1.f);
}
Expand Down Expand Up @@ -277,4 +279,4 @@ int main()
{
test();
Bench::bench();
}
}
55 changes: 17 additions & 38 deletions signals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,67 +272,46 @@ namespace fteng
return connect(func);
}

connection_raw connect(void(*func)(A...)) const
template<typename F>
connection_raw connect(F&& functor) const
{
using value_t = std::decay_t<F>;

size_t idx = conns.size();
auto& call = calls.emplace_back();
call.func = call.object = reinterpret_cast<void*>(func);
details::conn_base* conn = new details::conn_base(this, idx);
conns.emplace_back(conn);
return { conn };
}

template<typename F>
connection_raw connect(F&& functor) const
{
using f_type = std::remove_pointer_t<std::remove_reference_t<F>>;
if constexpr (std::is_convertible_v<f_type, void(*)(A...)>)
if constexpr (std::is_convertible_v<decltype(functor), void(*)(A...)>)
{
return connect(+functor);
}
else if constexpr (std::is_lvalue_reference_v<F>)
{
size_t idx = conns.size();
auto& call = calls.emplace_back();
call.func = reinterpret_cast<void*>(+[](void* obj, A ... args) { (*reinterpret_cast<f_type**>(obj))->operator()(args...); });
call.object = &functor;
void(*func)(A...) = functor;
call.func = call.object = reinterpret_cast<void*>(func);
details::conn_base* conn = new details::conn_base(this, idx);
conns.emplace_back(conn);
return { conn };
}
else if constexpr (sizeof(std::remove_pointer_t<f_type>) <= sizeof(void*))
else if constexpr (sizeof(value_t) <= sizeof(void*) && alignof(value_t) <= alignof(void*))
{
//copy the functor.
size_t idx = conns.size();
auto& call = calls.emplace_back();
call.func = reinterpret_cast<void*>(+[](void* obj, A ... args) { reinterpret_cast<f_type*>(obj)->operator()(args...); });
new (&call.object) f_type(std::move(functor));
using conn_t = std::conditional_t<std::is_trivially_destructible_v<F>, details::conn_base, details::conn_nontrivial<F>>;
call.func = reinterpret_cast<void*>(+[](void *obj, A ... args) { reinterpret_cast<value_t *>(obj)->operator()(args...); });
new (&call.object) value_t(std::forward<F>(functor));
using conn_t = std::conditional_t<std::is_trivially_destructible_v<value_t>, details::conn_base, details::conn_nontrivial<value_t>>;
details::conn_base* conn = new conn_t(this, idx);
conns.emplace_back(conn);
return { conn };
}
else
{
struct unique
struct deleter
{
f_type* ptr;

unique(f_type* ptr) : ptr(ptr) {}
unique(const unique&) = delete;
unique(unique&&) = delete;
value_t* ptr;

~unique()
~deleter()
{
delete ptr;
}
};

size_t idx = conns.size();
auto& call = calls.emplace_back();
call.func = reinterpret_cast<void*>(+[](void* obj, A ... args) { reinterpret_cast<unique*>(obj)->ptr->operator()(args...); });
new (&call.object) unique{ new f_type(std::move(functor)) };
details::conn_base* conn = new details::conn_nontrivial<unique>(this, idx);
call.func = reinterpret_cast<void*>(+[](void *obj, A ... args) { reinterpret_cast<deleter*>(obj)->ptr->operator()(args...); });
new (&call.object) deleter{ new value_t(std::forward<F>(functor)) };
details::conn_base* conn = new details::conn_nontrivial<deleter>(this, idx);
conns.emplace_back(conn);
return { conn };
}
Expand Down

0 comments on commit 072b48b

Please sign in to comment.