mirror of https://github.com/oxen-io/lokinet
Vastly simplified llarp::util::memFn
There is a huge pile of unnecessary machinery here that can be solved with a few lambdas and some member function pointer type deduction.
This commit is contained in:
parent
b4440094b0
commit
fe61367a87
|
@ -31,11 +31,7 @@ set(LIB_UTIL_SRC
|
|||
util/logging/win32_logger.cpp
|
||||
util/lokinet_init.c
|
||||
util/mem.cpp
|
||||
util/meta/memfn_traits.cpp
|
||||
util/meta/memfn.cpp
|
||||
util/meta/object.cpp
|
||||
util/meta/traits.cpp
|
||||
util/meta/variant.cpp
|
||||
util/printer.cpp
|
||||
util/status.cpp
|
||||
util/str.cpp
|
||||
|
|
|
@ -1,88 +1,68 @@
|
|||
#ifndef LLARP_UTIL_MEMFN
|
||||
#define LLARP_UTIL_MEMFN
|
||||
|
||||
#include <util/meta/memfn_traits.hpp>
|
||||
#include <util/meta/object.hpp>
|
||||
#include <util/meta/traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
template < typename Obj >
|
||||
struct MemFnDereference
|
||||
// Wraps a member function and instance into a callable object that invokes
|
||||
// the method (non-const overload).
|
||||
template <
|
||||
typename Return, typename Class, typename Derived, typename... Arg,
|
||||
typename =
|
||||
std::enable_if_t< std::is_base_of< Class, Derived >::value > >
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...), Derived* self)
|
||||
{
|
||||
// clang-format off
|
||||
static inline Obj& derefImp(Obj& obj, std::false_type)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
return [f, self](Arg... args) {
|
||||
return (self->*f)(std::forward< Arg >(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
static inline Obj& derefImp(Type& obj, std::true_type)
|
||||
{
|
||||
return *obj;
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
static inline Obj& derefImp(const Type& obj, std::true_type)
|
||||
{
|
||||
return *obj;
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
static inline Obj& deref(Type& obj)
|
||||
{
|
||||
return derefImp(obj, traits::is_pointy< Type >());
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
static inline Obj& deref(const Type& obj)
|
||||
{
|
||||
return derefImp(obj, traits::is_pointy< Type >());
|
||||
}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Instance >
|
||||
class MemFn
|
||||
// Wraps a member function and instance into a lambda that invokes the
|
||||
// method (const overload).
|
||||
template <
|
||||
typename Return, typename Class, typename Derived, typename... Arg,
|
||||
typename =
|
||||
std::enable_if_t< std::is_base_of< Class, Derived >::value > >
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...) const, const Derived* self)
|
||||
{
|
||||
using traits = MemFnTraits< Prototype >;
|
||||
return [f, self](Arg... args) {
|
||||
return (self->*f)(std::forward< Arg >(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
static_assert(traits::IsMemFn, "");
|
||||
|
||||
public:
|
||||
using result_type = typename traits::result_type;
|
||||
|
||||
private:
|
||||
Prototype m_func;
|
||||
object::Proxy< Instance > m_instance;
|
||||
|
||||
using Deref = MemFnDereference< typename traits::class_type >;
|
||||
|
||||
public:
|
||||
MemFn(Prototype prototype, const Instance& instance)
|
||||
: m_func(prototype), m_instance(instance)
|
||||
{
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
result_type
|
||||
operator()(Args&&... args) const
|
||||
{
|
||||
return (Deref::deref(m_instance.value())
|
||||
.*m_func)(std::forward< Args >(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Instance >
|
||||
MemFn< Prototype, Instance >
|
||||
memFn(Prototype prototype, const Instance& instance)
|
||||
// Wraps a member function and shared pointer to an instance into a lambda
|
||||
// that invokes the method.
|
||||
template <
|
||||
typename Return, typename Class, typename Derived, typename... Arg,
|
||||
typename =
|
||||
std::enable_if_t< std::is_base_of< Class, Derived >::value > >
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...), std::shared_ptr< Derived > self)
|
||||
{
|
||||
return MemFn< Prototype, Instance >(prototype, instance);
|
||||
return [f, self = std::move(self)](Arg... args) {
|
||||
return (self.get()->*f)(std::forward< Arg >(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
// Wraps a member function and shared pointer to an instance into a lambda
|
||||
// that invokes the method (const method overload).
|
||||
template <
|
||||
typename Return, typename Class, typename Derived, typename... Arg,
|
||||
typename =
|
||||
std::enable_if_t< std::is_base_of< Class, Derived >::value > >
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...) const, std::shared_ptr< Derived > self)
|
||||
{
|
||||
return [f, self = std::move(self)](Arg... args) {
|
||||
return (self.get()->*f)(std::forward< Arg >(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include <util/meta/memfn_traits.hpp>
|
|
@ -1,107 +0,0 @@
|
|||
#ifndef LLARP_UTIL_MEMFN_TRAITS
|
||||
#define LLARP_UTIL_MEMFN_TRAITS
|
||||
|
||||
#include <util/meta/object.hpp>
|
||||
#include <util/meta/traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
template < typename Prototype, typename TestPrototype >
|
||||
struct MemFnTraitsImpl;
|
||||
|
||||
template < typename Prototype >
|
||||
struct MemFnTraits : public MemFnTraitsImpl< Prototype, Prototype >
|
||||
{
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Return, typename Type,
|
||||
typename... Args >
|
||||
class MemFnTraitsClass
|
||||
{
|
||||
using NonCVTag = traits::Tag< 0 >;
|
||||
using ConstTag = traits::Tag< 1 >;
|
||||
using VolTag = traits::Tag< 2 >;
|
||||
using ConstVolTag = traits::Tag< 3 >;
|
||||
|
||||
// clang-format off
|
||||
static constexpr NonCVTag test(Return (Type::*)(Args...));
|
||||
static constexpr ConstTag test(Return (Type::*)(Args...) const);
|
||||
static constexpr VolTag test(Return (Type::*)(Args...) volatile);
|
||||
static constexpr ConstVolTag test(Return (Type::*)(Args...) const volatile);
|
||||
// clang-format on
|
||||
public:
|
||||
static constexpr bool IsConst =
|
||||
((sizeof((test)((Prototype)0)) - 1) & 1) != 0;
|
||||
static constexpr bool IsVolatile =
|
||||
((sizeof((test)((Prototype)0)) - 1) & 2) != 0;
|
||||
|
||||
using ctype = std::conditional_t< IsConst, const Type, Type >;
|
||||
using type = std::conditional_t< IsVolatile, volatile ctype, ctype >;
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Return, typename Type,
|
||||
typename... Args >
|
||||
struct MemFnTraitsImpl< Prototype, Return (Type::*)(Args...) >
|
||||
{
|
||||
static constexpr bool IsMemFn = true;
|
||||
|
||||
using class_type =
|
||||
typename MemFnTraitsClass< Prototype, Return, Type, Args... >::type;
|
||||
|
||||
using result_type = Return;
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Return, typename Type,
|
||||
typename... Args >
|
||||
struct MemFnTraitsImpl< Prototype, Return (Type::*)(Args...) const >
|
||||
{
|
||||
static constexpr bool IsMemFn = true;
|
||||
|
||||
using class_type =
|
||||
typename MemFnTraitsClass< Prototype, Return, Type, Args... >::type;
|
||||
|
||||
using result_type = Return;
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Return, typename Type,
|
||||
typename... Args >
|
||||
struct MemFnTraitsImpl< Prototype, Return (Type::*)(Args...) volatile >
|
||||
{
|
||||
static constexpr bool IsMemFn = true;
|
||||
|
||||
using class_type =
|
||||
typename MemFnTraitsClass< Prototype, Return, Type, Args... >::type;
|
||||
|
||||
using result_type = Return;
|
||||
};
|
||||
|
||||
template < typename Prototype, typename Return, typename Type,
|
||||
typename... Args >
|
||||
struct MemFnTraitsImpl< Prototype,
|
||||
Return (Type::*)(Args...) const volatile >
|
||||
{
|
||||
static constexpr bool IsMemFn = true;
|
||||
|
||||
using class_type =
|
||||
typename MemFnTraitsClass< Prototype, Return, Type, Args... >::type;
|
||||
|
||||
using result_type = Return;
|
||||
};
|
||||
|
||||
template < typename Prototype, typename TestPrototype >
|
||||
struct MemFnTraitsImpl
|
||||
{
|
||||
static constexpr bool IsMemFn = false;
|
||||
|
||||
using result_type = void;
|
||||
using class_type = void;
|
||||
};
|
||||
} // namespace util
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
#include <util/meta/object.hpp>
|
|
@ -1,466 +0,0 @@
|
|||
#ifndef LLARP_OBJECT_HPP
|
||||
#define LLARP_OBJECT_HPP
|
||||
|
||||
#include <util/thread/threading.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include <nonstd/optional.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace object
|
||||
{
|
||||
/// Provide a buffer, capable of holding a single `Value` object.
|
||||
/// This is useful for node-based data structures.
|
||||
/// Note:
|
||||
/// - This union explicitly does *not* manage the lifetime of the object,
|
||||
/// explicit calls to the constructor/destructor must be made.
|
||||
template < typename Value >
|
||||
union Buffer {
|
||||
private:
|
||||
char m_buffer[sizeof(Value)];
|
||||
char m_align[alignof(Value)];
|
||||
|
||||
public:
|
||||
Value*
|
||||
address()
|
||||
{
|
||||
return reinterpret_cast< Value* >(static_cast< void* >(m_buffer));
|
||||
}
|
||||
const Value*
|
||||
address() const
|
||||
{
|
||||
return reinterpret_cast< Value* >(static_cast< void* >(m_buffer));
|
||||
}
|
||||
|
||||
char*
|
||||
buffer()
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
const char*
|
||||
buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
Value&
|
||||
value()
|
||||
{
|
||||
return *reinterpret_cast< Value* >(this);
|
||||
}
|
||||
const Value&
|
||||
value() const
|
||||
{
|
||||
return *reinterpret_cast< const Value* >(this);
|
||||
}
|
||||
};
|
||||
|
||||
template < typename Value >
|
||||
class Proxy
|
||||
{
|
||||
Buffer< Value > m_value;
|
||||
|
||||
Proxy&
|
||||
operator=(const Proxy&) = delete;
|
||||
|
||||
public:
|
||||
Proxy()
|
||||
{
|
||||
::new(m_value.buffer()) Value();
|
||||
}
|
||||
|
||||
Proxy(const Proxy& other)
|
||||
{
|
||||
::new(m_value.buffer()) Value(other.value());
|
||||
}
|
||||
|
||||
Proxy(const Value& value)
|
||||
{
|
||||
::new(m_value.buffer()) Value(value);
|
||||
}
|
||||
|
||||
// template < typename... Args >
|
||||
// Proxy(Args&&... args)
|
||||
// {
|
||||
// ::new(m_value.buffer()) Value(std::forward< Args >(args)...);
|
||||
// }
|
||||
|
||||
~Proxy()
|
||||
{
|
||||
m_value.address()->~Value();
|
||||
}
|
||||
|
||||
Value&
|
||||
value()
|
||||
{
|
||||
return m_value.value();
|
||||
}
|
||||
|
||||
const Value&
|
||||
value() const
|
||||
{
|
||||
return m_value.value();
|
||||
}
|
||||
};
|
||||
|
||||
template < typename Value >
|
||||
class Catalog;
|
||||
template < typename Value >
|
||||
class CatalogIterator;
|
||||
|
||||
template < typename Value >
|
||||
class CatalogCleaner
|
||||
{
|
||||
Catalog< Value >* m_catalog;
|
||||
typename Catalog< Value >::Node* m_node;
|
||||
bool m_shouldDelete;
|
||||
|
||||
CatalogCleaner(const CatalogCleaner&) = delete;
|
||||
CatalogCleaner&
|
||||
operator=(const CatalogCleaner&) = delete;
|
||||
|
||||
public:
|
||||
explicit CatalogCleaner(Catalog< Value >* catalog)
|
||||
: m_catalog(catalog), m_node(nullptr), m_shouldDelete(false)
|
||||
{
|
||||
}
|
||||
|
||||
~CatalogCleaner();
|
||||
|
||||
void
|
||||
manageNode(typename Catalog< Value >::Node* node, bool shouldDelete)
|
||||
{
|
||||
m_node = node;
|
||||
m_shouldDelete = shouldDelete;
|
||||
}
|
||||
|
||||
void
|
||||
releaseNode()
|
||||
{
|
||||
m_node = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
release()
|
||||
{
|
||||
releaseNode();
|
||||
m_catalog = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// A pooling catalog of objects, referred to by a 32-bit handle
|
||||
template < typename Value >
|
||||
class Catalog
|
||||
{
|
||||
enum
|
||||
{
|
||||
INDEX_MASK = 0X007FFFFF,
|
||||
BUSY_INDICATOR = 0x00800000,
|
||||
GENERATION_INC = 0x01000000,
|
||||
GENERATION_MASK = 0XFF000000
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
union Payload {
|
||||
Buffer< Value > m_buffer;
|
||||
Node* m_next;
|
||||
};
|
||||
Payload m_payload;
|
||||
int32_t m_handle;
|
||||
};
|
||||
|
||||
std::vector< Node* > m_nodes GUARDED_BY(m_mutex);
|
||||
Node* m_next;
|
||||
std::atomic_size_t m_size;
|
||||
|
||||
mutable util::Mutex m_mutex;
|
||||
|
||||
friend class CatalogCleaner< Value >;
|
||||
friend class CatalogIterator< Value >;
|
||||
|
||||
static Value*
|
||||
getValue(Node* node)
|
||||
{
|
||||
return node->m_payload.m_buffer.address();
|
||||
}
|
||||
|
||||
void
|
||||
freeNode(Node* node)
|
||||
{
|
||||
node->m_handle += GENERATION_INC;
|
||||
node->m_handle &= ~BUSY_INDICATOR;
|
||||
|
||||
node->m_payload.m_next = m_next;
|
||||
m_next = node;
|
||||
}
|
||||
|
||||
Node*
|
||||
findNode(int32_t handle) const REQUIRES_SHARED(m_mutex)
|
||||
{
|
||||
int32_t index = handle & INDEX_MASK;
|
||||
|
||||
if(0 > index || index >= static_cast< int32_t >(m_nodes.size())
|
||||
|| !(handle & BUSY_INDICATOR))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node* node = m_nodes[index];
|
||||
|
||||
return (node->m_handle == handle) ? node : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
Catalog() : m_next(nullptr), m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Catalog()
|
||||
{
|
||||
removeAll();
|
||||
}
|
||||
|
||||
int32_t
|
||||
add(const Value& value)
|
||||
{
|
||||
int32_t handle;
|
||||
util::Lock l(m_mutex);
|
||||
CatalogCleaner< Value > guard(this);
|
||||
Node* node;
|
||||
|
||||
if(m_next)
|
||||
{
|
||||
node = m_next;
|
||||
m_next = node->m_payload.m_next;
|
||||
|
||||
guard.manageNode(node, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(m_nodes.size() < BUSY_INDICATOR);
|
||||
|
||||
node = new Node;
|
||||
guard.manageNode(node, true);
|
||||
|
||||
m_nodes.push_back(node);
|
||||
node->m_handle = static_cast< int32_t >(m_nodes.size() - 1);
|
||||
guard.manageNode(node, false);
|
||||
}
|
||||
|
||||
node->m_handle |= BUSY_INDICATOR;
|
||||
handle = node->m_handle;
|
||||
|
||||
// construct into the node.
|
||||
::new(getValue(node)) Value(value);
|
||||
|
||||
guard.release();
|
||||
|
||||
++m_size;
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool
|
||||
remove(int32_t handle, Value* value = nullptr)
|
||||
{
|
||||
util::Lock l(m_mutex);
|
||||
Node* node = findNode(handle);
|
||||
|
||||
if(!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value* val = getValue(node);
|
||||
|
||||
if(value)
|
||||
{
|
||||
*value = *val;
|
||||
}
|
||||
|
||||
val->~Value();
|
||||
freeNode(node);
|
||||
|
||||
--m_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
removeAll(std::vector< Value >* output = nullptr)
|
||||
{
|
||||
util::Lock l(m_mutex);
|
||||
|
||||
for(Node* node : m_nodes)
|
||||
{
|
||||
if(node->m_handle & BUSY_INDICATOR)
|
||||
{
|
||||
Value* value = getValue(node);
|
||||
|
||||
if(output)
|
||||
{
|
||||
output->push_back(*value);
|
||||
}
|
||||
value->~Value();
|
||||
}
|
||||
|
||||
delete node;
|
||||
}
|
||||
m_nodes.clear();
|
||||
m_next = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
replace(const Value& newValue, int32_t handle)
|
||||
{
|
||||
util::Lock l(m_mutex);
|
||||
Node* node = findNode(handle);
|
||||
|
||||
if(!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value* value = getValue(node);
|
||||
|
||||
value->~Value();
|
||||
// construct into the node.
|
||||
::new(value) Value(newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
nonstd::optional< Value >
|
||||
find(int32_t handle)
|
||||
{
|
||||
auto l = util::shared_lock(m_mutex);
|
||||
Node* node = findNode(handle);
|
||||
|
||||
if(!node)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return *getValue(node);
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/// introduced for testing only. verify the current state of the catalog.
|
||||
bool
|
||||
verify() const;
|
||||
};
|
||||
|
||||
template < typename Value >
|
||||
class SCOPED_CAPABILITY CatalogIterator
|
||||
{
|
||||
const Catalog< Value >* m_catalog;
|
||||
size_t m_index;
|
||||
|
||||
std::shared_lock<util::Mutex> lock;
|
||||
|
||||
CatalogIterator(const CatalogIterator&) = delete;
|
||||
CatalogIterator&
|
||||
operator=(const CatalogIterator&) = delete;
|
||||
|
||||
public:
|
||||
explicit CatalogIterator(const Catalog< Value >* catalog)
|
||||
ACQUIRE_SHARED(catalog->m_mutex)
|
||||
: m_catalog(catalog), m_index(-1), lock(m_catalog->m_mutex)
|
||||
{
|
||||
operator++();
|
||||
}
|
||||
|
||||
void
|
||||
operator++() NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
m_index++;
|
||||
while(m_index < m_catalog->m_nodes.size()
|
||||
&& !(m_catalog->m_nodes[m_index]->m_handle
|
||||
& Catalog< Value >::BUSY_INDICATOR))
|
||||
{
|
||||
m_index++;
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
return m_index < m_catalog->m_nodes.size();
|
||||
}
|
||||
|
||||
std::pair< int32_t, Value >
|
||||
operator()() const NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
auto* node = m_catalog->m_nodes[m_index];
|
||||
return {node->m_handle, *Catalog< Value >::getValue(node)};
|
||||
}
|
||||
};
|
||||
|
||||
template < typename Value >
|
||||
CatalogCleaner< Value >::~CatalogCleaner()
|
||||
{
|
||||
if(m_catalog && m_node)
|
||||
{
|
||||
if(m_shouldDelete)
|
||||
{
|
||||
// We call the destructor elsewhere.
|
||||
operator delete(m_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_catalog->freeNode(m_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < typename Value >
|
||||
bool
|
||||
Catalog< Value >::verify() const
|
||||
{
|
||||
util::Lock l(m_mutex);
|
||||
|
||||
if(m_nodes.size() < m_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t busyCount = 0;
|
||||
for(size_t i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
if((m_nodes[i]->m_handle & INDEX_MASK) != i)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(m_nodes[i]->m_handle & BUSY_INDICATOR)
|
||||
{
|
||||
busyCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_size != busyCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t freeCount = 0;
|
||||
for(Node* p = m_next; p != nullptr; p = p->m_payload.m_next)
|
||||
{
|
||||
freeCount++;
|
||||
}
|
||||
|
||||
if(freeCount + busyCount != m_nodes.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace object
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef LLARP_TRAITS_HPP
|
||||
#define LLARP_TRAITS_HPP
|
||||
|
||||
#include <absl/meta/type_traits.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
@ -12,9 +10,18 @@ namespace llarp
|
|||
{
|
||||
namespace traits
|
||||
{
|
||||
using absl::conjunction;
|
||||
using absl::disjunction;
|
||||
using absl::void_t;
|
||||
#ifdef __cpp_lib_void_t
|
||||
using std::void_t;
|
||||
#else
|
||||
/// C++17 void_t backport
|
||||
template < typename... Ts >
|
||||
struct void_t_impl
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
template < typename... Ts >
|
||||
using void_t = typename void_t_impl< Ts... >::type;
|
||||
#endif
|
||||
|
||||
/// Represents the empty type
|
||||
struct Bottom
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include <util/meta/variant.hpp>
|
|
@ -1,45 +0,0 @@
|
|||
#ifndef LLARP_VARIANT_HPP
|
||||
#define LLARP_VARIANT_HPP
|
||||
|
||||
#include <absl/types/variant.h>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
template < typename... Ts >
|
||||
struct _overloaded;
|
||||
|
||||
template < typename T, typename... Ts >
|
||||
struct _overloaded< T, Ts... > : T, _overloaded< Ts... >
|
||||
{
|
||||
_overloaded(T&& t, Ts&&... ts)
|
||||
: T(t), _overloaded< Ts... >(std::forward< Ts >(ts)...)
|
||||
{
|
||||
}
|
||||
using T::operator();
|
||||
|
||||
using _overloaded< Ts... >::operator();
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct _overloaded< T > : T
|
||||
{
|
||||
_overloaded(T&& t) : T(t)
|
||||
{
|
||||
}
|
||||
|
||||
using T::operator();
|
||||
};
|
||||
|
||||
template < typename... Ts >
|
||||
constexpr auto
|
||||
overloaded(Ts&&... ts) -> _overloaded< Ts... >
|
||||
{
|
||||
return _overloaded< Ts... >(std::forward< Ts >(ts)...);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <util/string_view.hpp>
|
||||
#include <util/meta/traits.hpp>
|
||||
#include <util/meta/variant.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <absl/types/variant.h>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
|
|
@ -32,7 +32,6 @@ list(APPEND TEST_SRC
|
|||
test_llarp_router_contact.cpp
|
||||
test_md5.cpp
|
||||
util/meta/test_llarp_util_memfn.cpp
|
||||
util/meta/test_llarp_util_object.cpp
|
||||
util/meta/test_llarp_util_traits.cpp
|
||||
util/test_llarp_util_aligned.cpp
|
||||
util/test_llarp_util_bencode.cpp
|
||||
|
|
|
@ -40,8 +40,8 @@ TEST(MemFn, call)
|
|||
ASSERT_EQ(11, util::memFn(&Foo::arg, &foo)(10));
|
||||
ASSERT_EQ(9, util::memFn(&Foo::constArg, &foo)(10));
|
||||
|
||||
ASSERT_TRUE(util::memFn(&Foo::constEmpty, foo)());
|
||||
ASSERT_EQ(9, util::memFn(&Foo::constArg, foo)(10));
|
||||
ASSERT_TRUE(util::memFn(&Foo::constEmpty, &foo)());
|
||||
ASSERT_EQ(9, util::memFn(&Foo::constArg, &foo)(10));
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
|
@ -54,7 +54,6 @@ TYPED_TEST_SUITE_P(MemFnType);
|
|||
TYPED_TEST_P(MemFnType, Smoke)
|
||||
{
|
||||
TypeParam foo{};
|
||||
ASSERT_TRUE(util::memFn(&Foo::constEmpty, foo)());
|
||||
ASSERT_TRUE(util::memFn(&Foo::constEmpty, &foo)());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
#include <util/meta/object.hpp>
|
||||
#include <util/thread/barrier.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace llarp::object;
|
||||
|
||||
TEST(Object, VerifySize)
|
||||
{
|
||||
static_assert(sizeof(Buffer< char >) == sizeof(char), "");
|
||||
static_assert(sizeof(Buffer< int >) == sizeof(int), "");
|
||||
static_assert(sizeof(Buffer< double >) == sizeof(double), "");
|
||||
static_assert(sizeof(Buffer< std::string >) == sizeof(std::string), "");
|
||||
}
|
||||
|
||||
TEST(Object, Inplace)
|
||||
{
|
||||
// Verify we can create and destroy a type with a non-trivial destructor
|
||||
Buffer< std::vector< std::string > > strBuf;
|
||||
new(strBuf.buffer()) std::vector< std::string >(100, "abc");
|
||||
strBuf.value().~vector();
|
||||
}
|
||||
|
||||
TEST(Catalog, smoke)
|
||||
{
|
||||
const double value1 = 1.0;
|
||||
const double value2 = 2.0;
|
||||
int32_t handle1 = -1;
|
||||
int32_t handle2 = -1;
|
||||
|
||||
Catalog< double > catalog;
|
||||
|
||||
handle1 = catalog.add(value1);
|
||||
catalog.remove(handle1);
|
||||
|
||||
for(size_t j = 0; j < 5; ++j)
|
||||
{
|
||||
for(size_t i = 1; i < 256; ++i)
|
||||
{
|
||||
ASSERT_FALSE(catalog.find(handle1));
|
||||
|
||||
handle2 = catalog.add(value2);
|
||||
catalog.remove(handle2);
|
||||
}
|
||||
handle2 = catalog.add(value2);
|
||||
|
||||
ASSERT_EQ(handle1, handle2);
|
||||
ASSERT_TRUE(catalog.find(handle1));
|
||||
|
||||
nonstd::optional< double > result = catalog.find(handle1);
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_EQ(value2, result);
|
||||
catalog.remove(handle2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Catalog, Iterator)
|
||||
{
|
||||
static constexpr size_t THREAD_COUNT = 10;
|
||||
static constexpr size_t ITERATION_COUNT = 1000;
|
||||
static constexpr int32_t MAX = std::numeric_limits< int32_t >::max();
|
||||
std::array< std::thread, THREAD_COUNT + 3 > threads;
|
||||
|
||||
using llarp::util::Barrier;
|
||||
using Iterator = CatalogIterator< int32_t >;
|
||||
using Cat = Catalog< int32_t >;
|
||||
|
||||
Barrier barrier(THREAD_COUNT + 3);
|
||||
Cat catalog;
|
||||
|
||||
// Repeatedly remove + add values from the catalog
|
||||
for(size_t i = 0; i < THREAD_COUNT; ++i)
|
||||
{
|
||||
threads[i] = std::thread(
|
||||
[](Barrier *b, Cat *cat, int32_t id) {
|
||||
b->Block();
|
||||
for(size_t j = 0; j < ITERATION_COUNT; ++j)
|
||||
{
|
||||
int32_t handle = cat->add(id);
|
||||
nonstd::optional< int32_t > res = cat->find(handle);
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.value(), id);
|
||||
ASSERT_TRUE(cat->replace(MAX - id, handle));
|
||||
res = cat->find(handle);
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(MAX - id, res.value());
|
||||
int32_t removed = 0;
|
||||
ASSERT_TRUE(cat->remove(handle, &removed));
|
||||
ASSERT_EQ(removed, MAX - id);
|
||||
ASSERT_FALSE(cat->find(handle));
|
||||
}
|
||||
},
|
||||
&barrier, &catalog, i);
|
||||
}
|
||||
|
||||
// Verify the length constraint is never violated
|
||||
threads[THREAD_COUNT] = std::thread(
|
||||
[](Barrier *b, Cat *cat) {
|
||||
b->Block();
|
||||
for(size_t i = 0; i < ITERATION_COUNT; ++i)
|
||||
{
|
||||
size_t size = cat->size();
|
||||
ASSERT_LE(size, THREAD_COUNT);
|
||||
}
|
||||
},
|
||||
&barrier, &catalog);
|
||||
|
||||
// Verify that iteration always produces a valid state
|
||||
threads[THREAD_COUNT + 1] = std::thread(
|
||||
[](Barrier *b, Cat *cat) {
|
||||
b->Block();
|
||||
for(size_t i = 0; i < ITERATION_COUNT; ++i)
|
||||
{
|
||||
int32_t arr[100];
|
||||
size_t size = 0;
|
||||
for(Iterator it(cat); it; ++it)
|
||||
{
|
||||
arr[size++] = it().second;
|
||||
}
|
||||
for(int32_t j = 0; j < 100; j++)
|
||||
{
|
||||
// value must be valid
|
||||
bool present = false;
|
||||
for(int32_t id = 0; id < static_cast< int32_t >(THREAD_COUNT); id++)
|
||||
{
|
||||
if(id == arr[j] || MAX - id == arr[j])
|
||||
{
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(void)present;
|
||||
|
||||
// no duplicate should be there
|
||||
for(size_t k = j + 1; k < size; k++)
|
||||
{
|
||||
ASSERT_NE(arr[j], arr[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
&barrier, &catalog);
|
||||
|
||||
// And that we don't have an invalid catalog
|
||||
threads[THREAD_COUNT + 2] = std::thread(
|
||||
[](Barrier *b, Cat *cat) {
|
||||
b->Block();
|
||||
for(size_t i = 0; i < ITERATION_COUNT; ++i)
|
||||
{
|
||||
cat->verify();
|
||||
}
|
||||
},
|
||||
&barrier, &catalog);
|
||||
|
||||
for(std::thread &t : threads)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue