Skip to content

Commit

Permalink
Merge pull request #21 from NoAvailableAlias/rework
Browse files Browse the repository at this point in the history
Rework. Closes #18 Closes #13 Closes #12
  • Loading branch information
NoAvailableAlias authored Aug 13, 2018
2 parents 34223a4 + ff714bc commit e110ecf
Show file tree
Hide file tree
Showing 20 changed files with 1,630 additions and 371 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2012-2015 ApEk, NoAvailableAlias, Nano-signal-slot Contributors
Copyright (c) 2012-2018 ApEk, NoAvailableAlias, Nano-signal-slot Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
88 changes: 49 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
nano-signal-slot
================

Pure C++11 Signals and Slots
Pure C++17 Signals and Slots

#### Include
```
// #include "nano_function.hpp" // Nano::Function, Nano::DelegateKey
// #include "nano_observer.hpp" // Nano::Observer
#include "nano_signal_slot.hpp" // Nano::Signal / All the above
// #include "nano_function.hpp" // Nano::Function, Nano::Delegate_Key
// #include "nano_mutex.hpp" // Nano::Noop_Mutex, Nano::Recursive_Mutex
// #include "nano_observer.hpp" // Nano::Observer
#include "nano_signal_slot.hpp" // Nano::Signal
```

#### Declare
Expand All @@ -18,60 +19,65 @@ Nano::Signal<bool(const char*, std::size_t)> signal_two;
```

#### Connect

```
// Connect member functions to Nano::signals;
signal_one.connect<Foo, &Foo::handler_a>(&foo);
signal_two.connect<Foo, &Foo::handler_b>(&foo);
// Connect member functions to Nano::Signals
signal_one.connect<&Foo::slot_member_one>(foo);
signal_two.connect<&Foo::slot_member_two>(foo);
// Connect overloaded member functions (required template syntax)
signal_one.connect<Foo, &Foo::slot_overloaded_member>(foo);
signal_two.connect<Foo, &Foo::slot_overloaded_member>(foo);
// Connect a static member function
signal_one.connect<Foo::handler_c>();
signal_one.connect<&Foo::slot_static_member_one>();
// Connect a free function
signal_two.connect<handler_d>();
signal_two.connect<&slot_free_function_one>();
```

#### Emit / Emit Accumulate

#### Fire / Fire Accumulate
```
// Emit Signals
signal_one.emit("we get signal");
signal_two.emit("main screen turn on", __LINE__);
// Fire Signals
signal_one.fire("we get signal");
signal_two.fire("main screen turn on", __LINE__);
std::vector<bool> status;
// Emit Signals and accumulate SRVs (signal return values)
signal_one.emit_accumulate([&](bool srv)
std::vector<bool> statuses;
auto accumulator = [&](bool srv)
{
status.push_back(srv);
}
,"how are you gentlemen");
statuses.push_back(srv);
};
// Fire Signals and accumulate SRVs (signal return values)
signal_one.fire_accumulate(accumulator, "how are you gentlemen");
```

#### Disconnect

_Additionally test convenience overloads for references._

```
// Disconnect member functions from Nano::Signals
signal_one.disconnect<Foo, &Foo::handler_a>(foo);
signal_two.disconnect<Foo, &Foo::handler_b>(foo);
signal_one.disconnect<&Foo::slot_member_one>(foo);
signal_two.disconnect<&Foo::slot_member_two>(foo);
// Connect overloaded member functions (required template syntax)
signal_one.disconnect<Foo, &Foo::slot_overloaded_member>(foo);
// Disconnect a static member function
signal_one.disconnect<Foo::handler_c>();
signal_one.disconnect<&Foo::slot_static_member_one>();
// Disconnect a free function
signal_two.disconnect<handler_d>();
signal_two.disconnect<&slot_free_function_one>();
// Disconnect all slots
signal_two.disconnect_all();
```

#### Connection Management

_To utilize automatic connection management you must inherit from Nano::Observer._
_Automatic connection management requires public inheritance from Nano::Observer<>._

```
struct Foo : public Nano::Observer
struct Foo : public Nano::Observer<>
{
bool handler_a(const char* sl) const
bool slot_member_one(const char* sl) const
{
std::cout << sl << std::endl;
return true;
Expand All @@ -81,10 +87,11 @@ struct Foo : public Nano::Observer

#### Function Objects

_*Must guarantee that object lifetimes are compatible.*_
**_Connected function objects must live longer than the connected signal._**
<br/>
_(be sure to disconnect the function object prior to it destructing)_

```
// Test using function objects
auto fo = [&](const char* sl)
{
std::cout << sl << std::endl;
Expand All @@ -93,16 +100,19 @@ auto fo = [&](const char* sl)
...
// Connecting function objects (or any object defining a suitable operator())
signal_one.connect(&fo);
// Connect any object that defines a suitable operator()
signal_one.connect(fo);
...
// Disconnecting function objects (convenience overload is used here)
// Disconnect that same functor instance
signal_one.disconnect(fo);
```

#### Links

| [Performance](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#performance) | [Metrics](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#metrics) | [Benchmark Algorithms](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#benchmark-algorithms) |
|:-------------------------------------------------------------------------------------------------- |:------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------:|
*_Benchmarks contain both the old nano-signal-slot v1.x scores as well as the v2.x scores._

| [Benchmark Results](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#signal-slot-benchmarks) | [Benchmark Algorithms](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#benchmark-algorithms) | [Unit Tests](https://github.com/NoAvailableAlias/nano-signal-slot/tree/master/tests/#unit-tests) |
|:-------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------:|
<br/>
3 changes: 2 additions & 1 deletion benchmark/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Benchmarks Have Moved
=====================

The comprehensive benchmarks have been moved to a new project [signal-slot-benchmarks](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#performance). Because links to this page still exist, this readme will remain here for the time being.
The comprehensive benchmarks have been moved to a new project [signal-slot-benchmarks](https://github.com/NoAvailableAlias/signal-slot-benchmarks/tree/master/#performance).
Because links to this page still exist, this readme will remain here for the time being.
91 changes: 52 additions & 39 deletions nano_function.hpp
Original file line number Diff line number Diff line change
@@ -1,73 +1,86 @@
#ifndef NANO_FUNCTION_HPP
#define NANO_FUNCTION_HPP
#pragma once

#include <cstdint>
#include <array>

namespace Nano
{

using DelegateKey = std::array<std::uintptr_t, 2>;
using Delegate_Key = std::array<const std::uintptr_t, 2>;

template <typename RT> class Function;
template <typename RT, typename... Args>
class Function<RT(Args...)>
class Function<RT(Args...)> final
{
using Thunk = RT (*)(void*, Args...);
// Only Nano::Observer is allowed access
template <typename> friend class Observer;

friend class Observer;
using Thunk = RT(*)(void*, Args&&...);

void* m_this_ptr; // instance pointer
Thunk m_stub_ptr; // free function pointer

Function(void* this_ptr, Thunk stub_ptr) :
m_this_ptr(this_ptr), m_stub_ptr(stub_ptr) {}

Function(DelegateKey delegate_key) :
m_this_ptr(reinterpret_cast<void*>(delegate_key[0])),
m_stub_ptr(reinterpret_cast<Thunk>(delegate_key[1])) {}
static inline Function bind(Delegate_Key const& delegate_key)
{
return
{
reinterpret_cast<void*>(delegate_key[0]),
reinterpret_cast<Thunk>(delegate_key[1])
};
}

public:

template <RT (*fun_ptr) (Args...)>
void* instance_pointer;
const Thunk function_pointer;

template <auto fun_ptr>
static inline Function bind()
{
return { nullptr, [](void* /*NULL*/, Args... args)
{ return (*fun_ptr)(std::forward<Args>(args)...); } };
}
template <typename T, RT (T::* mem_ptr) (Args...)>
static inline Function bind(T* pointer)
{
return { pointer, [](void* this_ptr, Args... args)
{ return (static_cast<T*>(this_ptr)->*mem_ptr) (std::forward<Args>(args)...); } };
return
{
nullptr, [](void* /*NULL*/, Args&&... args)
{
return (*fun_ptr)(std::forward<Args>(args)...);
}
};
}
template <typename T, RT (T::* mem_ptr) (Args...) const>

template <auto mem_ptr, typename T>
static inline Function bind(T* pointer)
{
return { pointer, [](void* this_ptr, Args... args)
{ return (static_cast<T*>(this_ptr)->*mem_ptr) (std::forward<Args>(args)...); } };
return
{
pointer, [](void* this_ptr, Args&&... args)
{
return (static_cast<T*>(this_ptr)->*mem_ptr)(std::forward<Args>(args)...);
}
};
}

template <typename L>
static inline Function bind(L* pointer)
{
return { pointer, [](void *this_ptr, Args... args)
{ return (static_cast<L*>(this_ptr)->operator()(std::forward<Args>(args)...)); }};
}
inline operator DelegateKey() const
{
return
{{
reinterpret_cast<std::uintptr_t>(m_this_ptr),
reinterpret_cast<std::uintptr_t>(m_stub_ptr)
}};
{
pointer, [](void *this_ptr, Args&&... args)
{
return static_cast<L*>(this_ptr)->operator()(std::forward<Args>(args)...);
}
};
}

template <typename... Uref>
inline RT operator() (Uref&&... args)
{
return (*m_stub_ptr)(m_this_ptr, std::forward<Uref>(args)...);
return (*function_pointer)(instance_pointer, std::forward<Uref>(args)...);
}

inline operator Delegate_Key() const
{
return
{
reinterpret_cast<std::uintptr_t>(instance_pointer),
reinterpret_cast<std::uintptr_t>(function_pointer)
};
}
};

} // namespace Nano ------------------------------------------------------------

#endif // NANO_FUNCTION_HPP
94 changes: 94 additions & 0 deletions nano_mutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#pragma once

#include <atomic>
#include <thread>
#include <cassert>

namespace Nano
{

// When using a sane optimization level,
// the use of this class with std::shared_lock
// or std::unique_lock will be completely optimized away.

class Noop_Mutex
{
public:

constexpr bool lock() const
{
return true;
}

constexpr bool try_lock() const
{
return true;
}

constexpr void unlock() noexcept
{

}

constexpr void lock_shared()
{

}

constexpr bool try_lock_shared() const
{
return true;
}

constexpr void unlock_shared()
{

}
};

//------------------------------------------------------------------------------

class Recursive_Mutex
{
std::atomic<std::thread::id> owner_thread_id = std::thread::id();
std::uint32_t recursive_counter = 0;
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;

public:

bool try_lock()
{
if (!lock_flag.test_and_set(std::memory_order_acquire))
{
owner_thread_id.store(std::this_thread::get_id(), std::memory_order_release);
}
else if (owner_thread_id.load(std::memory_order_acquire) != std::this_thread::get_id())
{
return false;
}
++recursive_counter;
return true;
}

void lock()
{
while (!try_lock())
{
std::this_thread::yield();
}
}

void unlock()
{
assert(owner_thread_id.load(std::memory_order_acquire) == std::this_thread::get_id());
assert(recursive_counter > 0);

if (--recursive_counter == 0)
{
owner_thread_id.store(std::thread::id(), std::memory_order_release);
lock_flag.clear(std::memory_order_release);
}
}
};

} // namespace Nano ------------------------------------------------------------
Loading

0 comments on commit e110ecf

Please sign in to comment.