From 87c18aab8c32d4ec07ea166721ae847b72f7418a Mon Sep 17 00:00:00 2001
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Sun, 12 Jul 2020 23:27:07 +0300
Subject: libcamera: flags: Add type-safe enum-based flags
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a Flags template class that provide type-safe bitwise operators on
enum values. This allows using enum types for bit fields, without giving
away type-safety as usually done when storing combined flags in integer
variables.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
---
 include/libcamera/base/flags.h     | 195 +++++++++++++++++++++++++++++++++++++
 include/libcamera/base/meson.build |   1 +
 2 files changed, 196 insertions(+)
 create mode 100644 include/libcamera/base/flags.h

(limited to 'include')

diff --git a/include/libcamera/base/flags.h b/include/libcamera/base/flags.h
new file mode 100644
index 00000000..adec549d
--- /dev/null
+++ b/include/libcamera/base/flags.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * flags.h - Type-safe enum-based bitfields
+ */
+#ifndef __LIBCAMERA_BASE_FLAGS_H__
+#define __LIBCAMERA_BASE_FLAGS_H__
+
+#include <type_traits>
+
+namespace libcamera {
+
+template<typename E>
+class Flags
+{
+public:
+	static_assert(std::is_enum<E>::value,
+		      "Flags<> template parameter must be an enum");
+
+	using Type = std::underlying_type_t<E>;
+
+	constexpr Flags()
+		: value_(0)
+	{
+	}
+
+	constexpr Flags(E flag)
+		: value_(static_cast<Type>(flag))
+	{
+	}
+
+	constexpr Flags &operator&=(E flag)
+	{
+		value_ &= static_cast<Type>(flag);
+		return *this;
+	}
+
+	constexpr Flags &operator&=(Flags other)
+	{
+		value_ &= other.value_;
+		return *this;
+	}
+
+	constexpr Flags &operator|=(E flag)
+	{
+		value_ |= static_cast<Type>(flag);
+		return *this;
+	}
+
+	constexpr Flags &operator|=(Flags other)
+	{
+		value_ |= other.value_;
+		return *this;
+	}
+
+	constexpr Flags &operator^=(E flag)
+	{
+		value_ ^= static_cast<Type>(flag);
+		return *this;
+	}
+
+	constexpr Flags &operator^=(Flags other)
+	{
+		value_ ^= other.value_;
+		return *this;
+	}
+
+	constexpr bool operator==(E flag)
+	{
+		return value_ == static_cast<Type>(flag);
+	}
+
+	constexpr bool operator==(Flags other)
+	{
+		return value_ == static_cast<Type>(other);
+	}
+
+	constexpr bool operator!=(E flag)
+	{
+		return value_ != static_cast<Type>(flag);
+	}
+
+	constexpr bool operator!=(Flags other)
+	{
+		return value_ != static_cast<Type>(other);
+	}
+
+	constexpr explicit operator Type() const
+	{
+		return value_;
+	}
+
+	constexpr explicit operator bool() const
+	{
+		return !!value_;
+	}
+
+	constexpr Flags operator&(E flag) const
+	{
+		return Flags(static_cast<E>(value_ & static_cast<Type>(flag)));
+	}
+
+	constexpr Flags operator&(Flags other) const
+	{
+		return Flags(static_cast<E>(value_ & other.value_));
+	}
+
+	constexpr Flags operator|(E flag) const
+	{
+		return Flags(static_cast<E>(value_ | static_cast<Type>(flag)));
+	}
+
+	constexpr Flags operator|(Flags other) const
+	{
+		return Flags(static_cast<E>(value_ | other.value_));
+	}
+
+	constexpr Flags operator^(E flag) const
+	{
+		return Flags(static_cast<E>(value_ ^ static_cast<Type>(flag)));
+	}
+
+	constexpr Flags operator^(Flags other) const
+	{
+		return Flags(static_cast<E>(value_ ^ other.value_));
+	}
+
+	constexpr Flags operator~() const
+	{
+		return Flags(static_cast<E>(~value_));
+	}
+
+	constexpr bool operator!() const
+	{
+		return !value_;
+	}
+
+private:
+	Type value_;
+};
+
+#ifndef __DOXYGEN__
+template<typename E>
+struct flags_enable_operators {
+	static const bool enable = false;
+};
+
+template<typename E>
+typename std::enable_if_t<flags_enable_operators<E>::enable, Flags<E>>
+operator|(E lhs, E rhs)
+{
+	using type = std::underlying_type_t<E>;
+	return Flags<E>(static_cast<E>(static_cast<type>(lhs) | static_cast<type>(rhs)));
+}
+
+template<typename E>
+typename std::enable_if_t<flags_enable_operators<E>::enable, Flags<E>>
+operator&(E lhs, E rhs)
+{
+	using type = std::underlying_type_t<E>;
+	return Flags<E>(static_cast<E>(static_cast<type>(lhs) & static_cast<type>(rhs)));
+}
+
+template<typename E>
+typename std::enable_if_t<flags_enable_operators<E>::enable, Flags<E>>
+operator^(E lhs, E rhs)
+{
+	using type = std::underlying_type_t<E>;
+	return Flags<E>(static_cast<E>(static_cast<type>(lhs) ^ static_cast<type>(rhs)));
+}
+
+template<typename E>
+typename std::enable_if_t<flags_enable_operators<E>::enable, Flags<E>>
+operator~(E rhs)
+{
+	using type = std::underlying_type_t<E>;
+	return Flags<E>(static_cast<E>(~static_cast<type>(rhs)));
+}
+
+#define LIBCAMERA_FLAGS_ENABLE_OPERATORS(_enum)				\
+template<>								\
+struct flags_enable_operators<_enum> {					\
+	static const bool enable = true;				\
+};
+
+#else /* __DOXYGEN__ */
+
+#define LIBCAMERA_FLAGS_ENABLE_OPERATORS(_enum)
+
+#endif /* __DOXYGEN__ */
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_BASE_FLAGS_H__ */
diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
index 7c499b55..9feb4b93 100644
--- a/include/libcamera/base/meson.build
+++ b/include/libcamera/base/meson.build
@@ -9,6 +9,7 @@ libcamera_base_headers = files([
     'event_dispatcher_poll.h',
     'event_notifier.h',
     'file.h',
+    'flags.h',
     'log.h',
     'message.h',
     'object.h',
-- 
cgit v1.2.1