Added lots of small utilities

This commit is contained in:
2021-02-04 01:56:39 +01:00
parent 882383f762
commit b462eab362
9 changed files with 509 additions and 0 deletions

12
CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
set(headers
include/cleanuphelper.h
include/cppflags.h
include/cppmacros.h
include/cppoverloadutils.h
include/cppsignal.h
include/cpptypesafeenum.h
include/cpputils.h
include/delayedconstruction.h
)
idf_component_register(INCLUDE_DIRS include SRCS ${headers})

36
include/cleanuphelper.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
// system includes
#include <utility>
#include <optional>
namespace cpputils {
template<typename T>
class CleanupHelper
{
public:
CleanupHelper(T && cleanup) :
m_cleanup{std::move(cleanup)}
{}
~CleanupHelper()
{
if (m_cleanup)
(*m_cleanup)();
}
void disarm()
{
m_cleanup = std::nullopt;
}
private:
std::optional<T> m_cleanup;
};
template<typename T>
CleanupHelper<T> makeCleanupHelper(T && cleanup)
{
return CleanupHelper<T>(std::move(cleanup));
}
} // namespace cpputils

114
include/cppflags.h Normal file
View File

@ -0,0 +1,114 @@
#pragma once
// system includes
#include <type_traits>
#include <cstddef>
#include <utility>
#include <cstdint>
namespace cpputils {
class CppFlag
{
using uint = unsigned int;
using ushort = unsigned short;
int i;
public:
constexpr inline CppFlag(int value) noexcept : i(value) {}
constexpr inline operator int() const noexcept { return i; }
constexpr inline CppFlag(uint value) noexcept : i(int(value)) {}
constexpr inline CppFlag(short value) noexcept : i(int(value)) {}
constexpr inline CppFlag(ushort value) noexcept : i(int(uint(value))) {}
constexpr inline operator uint() const noexcept { return uint(i); }
};
template<typename Enum>
class CppFlags
{
static_assert((sizeof(Enum) <= sizeof(int)),
"CppFlags uses an int as storage, so an enum with underlying "
"long long will overflow.");
static_assert((std::is_enum<Enum>::value), "CppFlags is only usable on enumeration types.");
using uint = unsigned int;
using ushort = unsigned short;
public:
typedef typename std::conditional<
std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
unsigned int,
signed int
>::type Int;
typedef Enum enum_type;
constexpr inline CppFlags() noexcept : i(0) {}
constexpr inline CppFlags(Enum flags) noexcept : i(Int(flags)) {}
constexpr inline CppFlags(CppFlag flag) noexcept : i(flag) {}
constexpr inline CppFlags(std::initializer_list<Enum> flags) noexcept
: i(initializer_list_helper(flags.begin(), flags.end())) {}
constexpr inline CppFlags &operator&=(int mask) noexcept { i &= mask; return *this; }
constexpr inline CppFlags &operator&=(uint mask) noexcept { i &= mask; return *this; }
constexpr inline CppFlags &operator&=(Enum mask) noexcept { i &= Int(mask); return *this; }
constexpr inline CppFlags &operator|=(CppFlags other) noexcept { i |= other.i; return *this; }
constexpr inline CppFlags &operator|=(Enum other) noexcept { i |= Int(other); return *this; }
constexpr inline CppFlags &operator^=(CppFlags other) noexcept { i ^= other.i; return *this; }
constexpr inline CppFlags &operator^=(Enum other) noexcept { i ^= Int(other); return *this; }
constexpr inline operator Int() const noexcept { return i; }
constexpr inline CppFlags operator|(CppFlags other) const noexcept { return CppFlags(CppFlag(i | other.i)); }
constexpr inline CppFlags operator|(Enum other) const noexcept { return CppFlags(CppFlag(i | Int(other))); }
constexpr inline CppFlags operator^(CppFlags other) const noexcept { return CppFlags(CppFlag(i ^ other.i)); }
constexpr inline CppFlags operator^(Enum other) const noexcept { return CppFlags(CppFlag(i ^ Int(other))); }
constexpr inline CppFlags operator&(int mask) const noexcept { return CppFlags(CppFlag(i & mask)); }
constexpr inline CppFlags operator&(uint mask) const noexcept { return CppFlags(CppFlag(i & mask)); }
constexpr inline CppFlags operator&(Enum other) const noexcept { return CppFlags(CppFlag(i & Int(other))); }
constexpr inline CppFlags operator~() const noexcept { return CppFlags(CppFlag(~i)); }
constexpr inline bool operator!() const noexcept { return !i; }
constexpr inline bool testFlag(Enum flag) const noexcept { return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag) ); }
constexpr inline CppFlags &setFlag(Enum flag, bool on = true) noexcept
{
return on ? (*this |= flag) : (*this &= ~Int(flag));
}
private:
constexpr static inline Int initializer_list_helper(typename std::initializer_list<Enum>::const_iterator it,
typename std::initializer_list<Enum>::const_iterator end)
noexcept
{
return (it == end ? Int(0) : (Int(*it) | initializer_list_helper(it + 1, end)));
}
Int i;
};
class CppIncompatibleFlag
{
int i;
public:
constexpr inline explicit CppIncompatibleFlag(int i) noexcept;
constexpr inline operator int() const noexcept { return i; }
};
constexpr inline CppIncompatibleFlag::CppIncompatibleFlag(int value) noexcept : i(value) {}
} // namespace cpputils
#define CPP_DECLARE_FLAGS(Flags, Enum)\
typedef ::cpputils::CppFlags<Enum> Flags;
#define CPP_DECLARE_INCOMPATIBLE_FLAGS(Flags) \
constexpr inline ::cpputils::CppIncompatibleFlag operator|(Flags::enum_type f1, int f2) noexcept \
{ return ::cpputils::CppIncompatibleFlag(int(f1) | f2); }
#define CPP_DECLARE_OPERATORS_FOR_FLAGS(Flags) \
constexpr inline ::cpputils::CppFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) noexcept \
{ return ::cpputils::CppFlags<Flags::enum_type>(f1) | f2; } \
constexpr inline ::cpputils::CppFlags<Flags::enum_type> operator|(Flags::enum_type f1, ::cpputils::CppFlags<Flags::enum_type> f2) noexcept \
{ return f2 | f1; } CPP_DECLARE_INCOMPATIBLE_FLAGS(Flags)

28
include/cppmacros.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
// Collection of useful macros to make C++ code prettier
/*
Avoid "unused parameter" warnings
*/
#define CPP_UNUSED(x) (void)x;
/* These two macros make it possible to turn the builtin line expander into a
* string literal. */
#define CPP_STRINGIFY2(x) #x
#define CPP_STRINGIFY(x) CPP_STRINGIFY2(x)
/*
Some classes do not permit copies to be made of an object. These
classes contains a private copy constructor and assignment
operator to disable copying (the compiler gives an error message).
*/
#define CPP_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
#define CPP_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define CPP_DISABLE_COPY_MOVE(Class) \
CPP_DISABLE_COPY(Class) \
CPP_DISABLE_MOVE(Class)

View File

@ -0,0 +1,42 @@
#pragma once
namespace cpputils {
template <typename... Args>
struct CppNonConstOverload
{
template <typename R, typename T>
constexpr auto operator()(R (T::*ptr)(Args...)) const noexcept -> decltype(ptr)
{ return ptr; }
template <typename R, typename T>
static constexpr auto of(R (T::*ptr)(Args...)) noexcept -> decltype(ptr)
{ return ptr; }
};
template <typename... Args>
struct CppConstOverload
{
template <typename R, typename T>
constexpr auto operator()(R (T::*ptr)(Args...) const) const noexcept -> decltype(ptr)
{ return ptr; }
template <typename R, typename T>
static constexpr auto of(R (T::*ptr)(Args...) const) noexcept -> decltype(ptr)
{ return ptr; }
};
template <typename... Args>
struct CppOverload : CppConstOverload<Args...>, CppNonConstOverload<Args...>
{
using CppConstOverload<Args...>::of;
using CppConstOverload<Args...>::operator();
using CppNonConstOverload<Args...>::of;
using CppNonConstOverload<Args...>::operator();
template <typename R>
constexpr auto operator()(R (*ptr)(Args...)) const noexcept -> decltype(ptr)
{ return ptr; }
template <typename R>
static constexpr auto of(R (*ptr)(Args...)) noexcept -> decltype(ptr)
{ return ptr; }
};
template <typename... Args> constexpr __attribute__((__unused__)) CppOverload<Args...> cppOverload = {};
template <typename... Args> constexpr __attribute__((__unused__)) CppConstOverload<Args...> cppConstOverload = {};
template <typename... Args> constexpr __attribute__((__unused__)) CppNonConstOverload<Args...> cppNonConstOverload = {};
} // namespace cpputils

37
include/cppsignal.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
// system includes
#include <functional>
#include <vector>
#include <utility>
namespace cpputils {
template<typename... T>
class Signal
{
public:
using Slot = std::function<void(T...)>;
Signal &operator+=(Slot &&slot)
{
m_slots.emplace_back(std::move(slot));
return *this;
}
Signal &operator+=(const Slot &slot)
{
m_slots.emplace_back(slot);
return *this;
}
template<typename ...Targs>
void operator()(Targs && ...args) const
{
for (const auto &slot : m_slots)
slot(std::forward<Targs>(args)...);
}
private:
std::vector<Slot> m_slots;
};
} // namespace cpputils

41
include/cpptypesafeenum.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
// system includes
#include <optional>
#include <string>
// local includes
#include "cppmacros.h"
// These two macros make it possible to define a typesafe enum with parse and
// toString methods
#define DECLARE_TYPESAFE_ENUM_HELPER1(name) name,
#define DECLARE_TYPESAFE_ENUM_HELPER2(name) case TheEnum::name: return #name;
#define DECLARE_TYPESAFE_ENUM_HELPER3(name) else if (str == CPP_STRINGIFY(name)) return TheEnum::name;
#define DECLARE_TYPESAFE_ENUM(Name, Derivation, Values) \
enum class Name Derivation \
{ \
Values(DECLARE_TYPESAFE_ENUM_HELPER1) \
}; \
std::string toString(Name value); \
std::optional<Name> parse##Name(std::string_view str);
#define IMPLEMENT_TYPESAFE_ENUM(Name, Derivation, Values) \
std::string toString(Name value) \
{ \
switch (value) \
{ \
using TheEnum = Name; \
Values(DECLARE_TYPESAFE_ENUM_HELPER2) \
} \
return std::string{"Unknown " #Name "("} + std::to_string(int(value)) + ')'; \
} \
std::optional<Name> parse##Name(std::string_view str) \
{ \
using TheEnum = Name; \
if (false) {} \
Values(DECLARE_TYPESAFE_ENUM_HELPER3) \
return std::nullopt; \
}

100
include/cpputils.h Normal file
View File

@ -0,0 +1,100 @@
#pragma once
// system includes
#include <utility>
#include <type_traits>
namespace cpputils {
namespace literals {
namespace {
/**
* User-defined Literals
* usage:
*
* uint32_t = test = 10_MHz; // --> 10000000
*/
constexpr unsigned long long operator"" _kHz(unsigned long long x)
{
return x * 1000;
}
constexpr unsigned long long operator"" _MHz(unsigned long long x)
{
return x * 1000 * 1000;
}
constexpr unsigned long long operator"" _GHz(unsigned long long x)
{
return x * 1000 * 1000 * 1000;
}
constexpr unsigned long long operator"" _kBit(unsigned long long x)
{
return x * 1024;
}
constexpr unsigned long long operator"" _MBit(unsigned long long x)
{
return x * 1024 * 1024;
}
constexpr unsigned long long operator"" _GBit(unsigned long long x)
{
return x * 1024 * 1024 * 1024;
}
constexpr unsigned long long operator"" _kB(unsigned long long x)
{
return x * 1024;
}
constexpr unsigned long long operator"" _MB(unsigned long long x)
{
return x * 1024 * 1024;
}
constexpr unsigned long long operator"" _GB(unsigned long long x)
{
return x * 1024 * 1024 * 1024;
}
} // namespace
} // namespace literals
namespace {
template<typename T>
T vmin(T&&t)
{
return std::forward<T>(t);
}
template<typename T0, typename T1, typename... Ts>
typename std::common_type<T0, T1, Ts...>::type vmin(T0&& val1, T1&& val2, Ts&&... vs)
{
if (val1 < val2)
return vmin(val1, std::forward<Ts>(vs)...);
else
return vmin(val2, std::forward<Ts>(vs)...);
}
template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi )
{
// assert( !(hi < lo) );
return (v < lo) ? lo : (hi < v) ? hi : v;
}
template<typename T>
T mapValue(T x, T in_min, T in_max, T out_min, T out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
template<typename First, typename ... T>
bool is_in(First &&first, T && ... t)
{
return ((first == t) || ...);
}
} // namespace
} // namespace cpputils

View File

@ -0,0 +1,99 @@
#pragma once
// system includes
#include <cassert>
#include <type_traits>
#include <utility>
// local includes
#include "cppmacros.h"
namespace cpputils {
template<typename T>
class DelayedConstruction
{
CPP_DISABLE_COPY_MOVE(DelayedConstruction)
public:
DelayedConstruction() = default;
~DelayedConstruction()
{
if (m_constructed)
destruct();
}
template<typename ...Targs>
void construct(Targs &&...args)
{
assert(!m_constructed);
new (&helper.value) T {std::forward<Targs>(args)...};
m_constructed = true;
}
void destruct()
{
assert(m_constructed);
helper.value.~T();
m_constructed = false;
}
T &get()
{
assert(m_constructed);
return helper.value;
}
const T &get() const
{
assert(m_constructed);
return helper.value;
}
T *operator->()
{
assert(m_constructed);
return &helper.value;
}
const T *operator->() const
{
assert(m_constructed);
return &helper.value;
}
T &operator*()
{
assert(m_constructed);
return helper.value;
}
const T &operator*() const
{
assert(m_constructed);
return helper.value;
}
//! allows for getting the address before the object has been constructed
T &getUnsafe()
{
return helper.value;
}
//! allows for getting the address before the object has been constructed
const T &getUnsafe() const
{
return helper.value;
}
bool constructed() const { return m_constructed; }
private:
bool m_constructed{};
union Helper
{
Helper() {}
~Helper() {}
T value;
} helper;
};
} // namespace cpputils