From decd08b89565b18067d229c8c25b6f3a3333c653 Mon Sep 17 00:00:00 2001 From: Jim Flynn Date: Sun, 13 Mar 2022 22:35:46 +0000 Subject: IVGCVSW-6847 replace armnn:Optional with arm::pipe::Optional in profiling code Change-Id: I048c538d4f8c21770aec2b2751c934d9fa15a4dc Signed-off-by: Jim Flynn --- profiling/common/include/Optional.hpp | 314 ++++++++++++++++++++++++ profiling/common/include/ProfilingException.hpp | 5 + 2 files changed, 319 insertions(+) create mode 100644 profiling/common/include/Optional.hpp (limited to 'profiling/common/include') diff --git a/profiling/common/include/Optional.hpp b/profiling/common/include/Optional.hpp new file mode 100644 index 0000000000..e2d6c67e2a --- /dev/null +++ b/profiling/common/include/Optional.hpp @@ -0,0 +1,314 @@ +// +// Copyright © 2022 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include "ProfilingException.hpp" + +#include +#include + +/// Optional is a drop in replacement for std::optional until we migrate +/// to c++-17. Only a subset of the optional features are implemented that +/// we intend to use in ArmNN. + +/// There are two distinct implementations here: +/// +/// 1, for normal constructable/destructable types and reference types +/// 2, for reference types + +/// The std::optional features we support are: +/// +/// - has_value() and operator bool() to tell if the optional has a value +/// - value() returns a reference to the held object +/// + +namespace arm +{ + +namespace pipe +{ + +/// EmptyOptional is used to initialize the Optional class in case we want +/// to have default value for an Optional in a function declaration. +struct EmptyOptional {}; + +/// Disambiguation tag that can be passed to the constructor to indicate that +/// the contained object should be constructed in-place +struct ConstructInPlace +{ + explicit ConstructInPlace() = default; +}; + +#define ARM_PIPE_CONSTRUCT_IN_PLACE arm::pipe::ConstructInPlace{} + +/// OptionalBase is the common functionality between reference and non-reference +/// optional types. +class OptionalBase +{ +public: + OptionalBase() noexcept + : m_HasValue{false} + { + } + + bool has_value() const noexcept + { + return m_HasValue; + } + + /// Conversion to bool, so can be used in if-statements and similar contexts expecting a bool. + /// Note this is explicit so that it doesn't get implicitly converted to a bool in unwanted cases, + /// for example "Optional == Optional" should not compile. + explicit operator bool() const noexcept + { + return has_value(); + } + +protected: + OptionalBase(bool hasValue) noexcept + : m_HasValue{hasValue} + { + } + + bool m_HasValue; +}; + +/// +/// The default implementation is the non-reference case. This +/// has an unsigned char array for storing the optional value which +/// is in-place constructed there. +/// +template +class OptionalReferenceSwitch : public OptionalBase +{ +public: + using Base = OptionalBase; + + OptionalReferenceSwitch() noexcept : Base{} {} + OptionalReferenceSwitch(EmptyOptional) noexcept : Base{} {} + + OptionalReferenceSwitch(const T& value) + : Base{} + { + Construct(value); + } + + template + OptionalReferenceSwitch(ConstructInPlace, Args&&... args) + : Base{} + { + Construct(ARM_PIPE_CONSTRUCT_IN_PLACE, std::forward(args)...); + } + + OptionalReferenceSwitch(const OptionalReferenceSwitch& other) + : Base{} + { + *this = other; + } + + OptionalReferenceSwitch& operator=(const T& value) + { + reset(); + Construct(value); + return *this; + } + + OptionalReferenceSwitch& operator=(const OptionalReferenceSwitch& other) + { + reset(); + if (other.has_value()) + { + Construct(other.value()); + } + + return *this; + } + + OptionalReferenceSwitch& operator=(EmptyOptional) + { + reset(); + return *this; + } + + ~OptionalReferenceSwitch() + { + reset(); + } + + void reset() + { + if (Base::has_value()) + { + value().T::~T(); + Base::m_HasValue = false; + } + } + + const T& value() const + { + if (!Base::has_value()) + { + throw BadOptionalAccessException("Optional has no value"); + } + + auto valuePtr = reinterpret_cast(m_Storage); + return *valuePtr; + } + + T& value() + { + if (!Base::has_value()) + { + throw BadOptionalAccessException("Optional has no value"); + } + + auto valuePtr = reinterpret_cast(m_Storage); + return *valuePtr; + } + +private: + void Construct(const T& value) + { + new (m_Storage) T(value); + m_HasValue = true; + } + + template + void Construct(ConstructInPlace, Args&&... args) + { + new (m_Storage) T(std::forward(args)...); + m_HasValue = true; + } + + alignas(alignof(T)) unsigned char m_Storage[sizeof(T)]; +}; + +/// +/// This is the special case for reference types. This holds a pointer +/// to the referenced type. This doesn't own the referenced memory and +/// it never calls delete on the pointer. +/// +template +class OptionalReferenceSwitch : public OptionalBase +{ +public: + using Base = OptionalBase; + using NonRefT = typename std::remove_reference::type; + + OptionalReferenceSwitch() noexcept : Base{}, m_Storage{nullptr} {} + OptionalReferenceSwitch(EmptyOptional) noexcept : Base{}, m_Storage{nullptr} {} + + OptionalReferenceSwitch(const OptionalReferenceSwitch& other) : Base{} + { + *this = other; + } + + OptionalReferenceSwitch(T value) + : Base{true} + , m_Storage{&value} + { + } + + template + OptionalReferenceSwitch(ConstructInPlace, Args&&... args) = delete; + + OptionalReferenceSwitch& operator=(const T value) + { + m_Storage = &value; + Base::m_HasValue = true; + return *this; + } + + OptionalReferenceSwitch& operator=(const OptionalReferenceSwitch& other) + { + m_Storage = other.m_Storage; + Base::m_HasValue = other.has_value(); + return *this; + } + + OptionalReferenceSwitch& operator=(EmptyOptional) + { + reset(); + return *this; + } + + ~OptionalReferenceSwitch() + { + reset(); + } + + void reset() + { + Base::m_HasValue = false; + m_Storage = nullptr; + } + + const T value() const + { + if (!Base::has_value()) + { + throw BadOptionalAccessException("Optional has no value"); + } + + return *m_Storage; + } + + T value() + { + if (!Base::has_value()) + { + throw BadOptionalAccessException("Optional has no value"); + } + + return *m_Storage; + } + +private: + NonRefT* m_Storage; +}; + +template +class Optional final : public OptionalReferenceSwitch::value, T> +{ +public: + using BaseSwitch = OptionalReferenceSwitch::value, T>; + + Optional() noexcept : BaseSwitch{} {} + Optional(const T& value) : BaseSwitch{value} {} + Optional& operator=(const Optional& other) = default; + Optional(EmptyOptional empty) : BaseSwitch{empty} {} + Optional(const Optional& other) : BaseSwitch{other} {} + Optional(const BaseSwitch& other) : BaseSwitch{other} {} + + template + explicit Optional(ConstructInPlace, Args&&... args) : + BaseSwitch(ARM_PIPE_CONSTRUCT_IN_PLACE, std::forward(args)...) {} + + /// Two optionals are considered equal if they are both empty or both contain values which + /// themselves are considered equal (via their own == operator). + bool operator==(const Optional& rhs) const + { + if (!this->has_value() && !rhs.has_value()) + { + return true; + } + if (this->has_value() && rhs.has_value() && this->value() == rhs.value()) + { + return true; + } + return false; + } +}; + +/// Utility template that constructs an object of type T in-place and wraps +/// it inside an Optional object +template +Optional MakeOptional(Args&&... args) +{ + return Optional(ARM_PIPE_CONSTRUCT_IN_PLACE, std::forward(args)...); +} + +} // namespace pipe +} // namespace arm diff --git a/profiling/common/include/ProfilingException.hpp b/profiling/common/include/ProfilingException.hpp index 82b724aaa0..135798817a 100644 --- a/profiling/common/include/ProfilingException.hpp +++ b/profiling/common/include/ProfilingException.hpp @@ -70,6 +70,11 @@ public: using ProfilingException::ProfilingException; }; +class BadOptionalAccessException : public ProfilingException +{ + using ProfilingException::ProfilingException; +}; + class BufferExhaustion : public ProfilingException { public: -- cgit v1.2.1