/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** \file * Templates used to declare parameters. */ #ifndef C2PARAM_DEF_H_ #define C2PARAM_DEF_H_ #include <type_traits> #include <C2Param.h> /// \addtogroup Parameters /// @{ /* ======================== UTILITY TEMPLATES FOR PARAMETER DEFINITIONS ======================== */ /// \addtogroup internal /// @{ /// Helper class that checks if a type has equality and inequality operators. struct C2_HIDE _C2Comparable_impl { template<typename S, typename=decltype(S() == S())> static std::true_type TestEqual(int); template<typename> static std::false_type TestEqual(...); template<typename S, typename=decltype(S() != S())> static std::true_type TestNotEqual(int); template<typename> static std::false_type TestNotEqual(...); }; /** * Helper template that returns if a type has equality and inequality operators. * * Use as _C2Comparable<typename S>::value. */ template<typename S> struct C2_HIDE _C2Comparable : public std::integral_constant<bool, decltype(_C2Comparable_impl::TestEqual<S>(0))::value || decltype(_C2Comparable_impl::TestNotEqual<S>(0))::value> { }; /// Helper class that checks if a type has a CORE_INDEX constant. struct C2_HIDE _C2CoreIndexHelper_impl { template<typename S, int=S::CORE_INDEX> static std::true_type TestCoreIndex(int); template<typename> static std::false_type TestCoreIndex(...); }; /// Macro that defines and thus overrides a type's CORE_INDEX for a setting #define _C2_CORE_INDEX_OVERRIDE(coreIndex) \ public: \ enum : uint32_t { CORE_INDEX = coreIndex }; /// Helper template that adds a CORE_INDEX to a type if it does not have one (for testing) template<typename S, int CoreIndex> struct C2_HIDE _C2AddCoreIndex : public S { _C2_CORE_INDEX_OVERRIDE(CoreIndex) }; /** * \brief Helper class to check struct requirements for parameters. * * Features: * - verify default constructor, no virtual methods, and no equality operators. * - expose PARAM_TYPE, and non-flex FLEX_SIZE. */ template<typename S, int CoreIndex, unsigned TypeFlags> struct C2_HIDE _C2StructCheck { static_assert( std::is_default_constructible<S>::value, "C2 structure must have default constructor"); static_assert(!std::is_polymorphic<S>::value, "C2 structure must not have virtual methods"); static_assert(!_C2Comparable<S>::value, "C2 structure must not have operator== or !="); public: enum : uint32_t { PARAM_TYPE = CoreIndex | TypeFlags }; protected: enum : uint32_t { FLEX_SIZE = 0, }; }; /// Helper class that checks if a type has an integer FLEX_SIZE member. struct C2_HIDE _C2Flexible_impl { /// specialization for types that have a FLEX_SIZE member template<typename S, unsigned=S::FLEX_SIZE> static std::true_type TestFlexSize(int); template<typename> static std::false_type TestFlexSize(...); }; /// Helper template that returns if a type has an integer FLEX_SIZE member. template<typename S> struct C2_HIDE _C2Flexible : public std::integral_constant<bool, decltype(_C2Flexible_impl::TestFlexSize<S>(0))::value> { }; /// Macro to test if a type is flexible (has a FLEX_SIZE member). #define IF_FLEXIBLE(S) ENABLE_IF(_C2Flexible<S>::value) /// Shorthand for std::enable_if #define ENABLE_IF(cond) typename std::enable_if<cond>::type template<typename T, typename V=void> struct C2_HIDE _c2_enable_if_type { typedef V type; }; /// Helper template that exposes the flexible subtype of a struct. template<typename S, typename E=void> struct C2_HIDE _C2FlexHelper { typedef void FlexType; enum : uint32_t { FLEX_SIZE = 0 }; }; /// Specialization for flexible types. This only works if _FlexMemberType is public. template<typename S> struct C2_HIDE _C2FlexHelper<S, typename _c2_enable_if_type<typename S::_FlexMemberType>::type> { typedef typename _C2FlexHelper<typename S::_FlexMemberType>::FlexType FlexType; enum : uint32_t { FLEX_SIZE = _C2FlexHelper<typename S::_FlexMemberType>::FLEX_SIZE }; }; /// Specialization for flex arrays. template<typename S> struct C2_HIDE _C2FlexHelper<S[], typename std::enable_if<std::is_void<typename _C2FlexHelper<S>::FlexType>::value>::type> { typedef S FlexType; enum : uint32_t { FLEX_SIZE = sizeof(S) }; }; /** * \brief Helper class to check flexible struct requirements and add common operations. * * Features: * - expose CORE_INDEX and FieldList (this is normally inherited from the struct, but flexible * structs cannot be base classes and thus inherited from) * - disable copy assignment and construction (TODO: this is already done in the FLEX macro for the * flexible struct, so may not be needed here) */ template<typename S, int ParamIndex, unsigned TypeFlags> struct C2_HIDE _C2FlexStructCheck : // add flexible flag as _C2StructCheck defines PARAM_TYPE public _C2StructCheck<S, ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, TypeFlags> { public: enum : uint32_t { /// \hideinitializer CORE_INDEX = ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, ///< flexible struct core-index }; inline static const std::vector<C2FieldDescriptor> FieldList() { return S::FieldList(); } // default constructor needed because of the disabled copy constructor inline _C2FlexStructCheck() = default; protected: // cannot copy flexible params _C2FlexStructCheck(const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete; _C2FlexStructCheck& operator= (const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete; // constants used for helper methods enum : uint32_t { /// \hideinitializer FLEX_SIZE = _C2FlexHelper<S>::FLEX_SIZE, ///< size of flexible type /// \hideinitializer MAX_SIZE = (uint32_t)std::min((size_t)UINT32_MAX, SIZE_MAX), // TODO: is this always u32 max? /// \hideinitializer BASE_SIZE = sizeof(S) + sizeof(C2Param), ///< size of the base param }; /// returns the allocated size of this param with flexCount, or 0 if it would overflow. inline static size_t CalcSize(size_t flexCount, size_t size = BASE_SIZE) { if (flexCount <= (MAX_SIZE - size) / S::FLEX_SIZE) { return size + S::FLEX_SIZE * flexCount; } return 0; } /// dynamic new operator usable for params of type S inline void* operator new(size_t size, size_t flexCount) noexcept { // TODO: assert(size == BASE_SIZE); size = CalcSize(flexCount, size); if (size > 0) { return ::operator new(size); } return nullptr; } }; /// Define From() cast operators for params. #define DEFINE_CAST_OPERATORS(_Type) \ inline static _Type* From(C2Param *other) { \ return (_Type*)C2Param::IfSuitable( \ other, sizeof(_Type), _Type::PARAM_TYPE, _Type::FLEX_SIZE, \ (_Type::PARAM_TYPE & T::Index::DIR_UNDEFINED) != T::Index::DIR_UNDEFINED); \ } \ inline static const _Type* From(const C2Param *other) { \ return const_cast<const _Type*>(From(const_cast<C2Param *>(other))); \ } \ inline static _Type* From(std::nullptr_t) { return nullptr; } \ /** * Define flexible allocators (AllocShared or AllocUnique) for flexible params. * - P::AllocXyz(flexCount, args...): allocate for given flex-count. This maps to * T(flexCount, args...)\ * * Clang does not support args... followed by templated param as args... eats it. Hence, * provide specializations where the initializer replaces the flexCount. * * Specializations that deduce flexCount: * - P::AllocXyz(T[], args...): allocate for size of (and with) init array. * - P::AllocXyz(std::initializer_list<T>, args...): allocate for size of (and with) initializer * list. * - P::AllocXyz(std::vector<T>, args...): allocate for size of (and with) init vector. * These specializations map to T(flexCount = size-of-init, args..., init) */ #define DEFINE_FLEXIBLE_ALLOC(_Type, S, ptr, Ptr) \ template<typename ...Args> \ inline static std::ptr##_ptr<_Type> Alloc##Ptr(size_t flexCount, const Args(&... args)) { \ return std::ptr##_ptr<_Type>(new(flexCount) _Type(flexCount, args...)); \ } \ template<typename ...Args, typename U=typename S::FlexType> \ inline static std::ptr##_ptr<_Type> Alloc##Ptr( \ const std::initializer_list<U> &init, const Args(&... args)) { \ return std::ptr##_ptr<_Type>(new(init.size()) _Type(init.size(), args..., init)); \ } \ template<typename ...Args, typename U=typename S::FlexType> \ inline static std::ptr##_ptr<_Type> Alloc##Ptr( \ const std::vector<U> &init, const Args(&... args)) { \ return std::ptr##_ptr<_Type>(new(init.size()) _Type(init.size(), args..., init)); \ } \ template<typename ...Args, typename U=typename S::FlexType, unsigned N> \ inline static std::ptr##_ptr<_Type> Alloc##Ptr(const U(&init)[N], const Args(&... args)) { \ return std::ptr##_ptr<_Type>(new(N) _Type(N, args..., init)); \ } \ /** * Define flexible methods AllocShared, AllocUnique and flexCount. */ #define DEFINE_FLEXIBLE_METHODS(_Type, S) \ DEFINE_FLEXIBLE_ALLOC(_Type, S, shared, Shared) \ DEFINE_FLEXIBLE_ALLOC(_Type, S, unique, Unique) \ inline size_t flexCount() const { \ static_assert(sizeof(_Type) == _Type::BASE_SIZE, "incorrect BASE_SIZE"); \ size_t sz = this->size(); \ if (sz >= sizeof(_Type)) { \ return (sz - sizeof(_Type)) / _Type::FLEX_SIZE; \ } \ return 0; \ } \ /// Mark flexible member variable and make structure flexible. #define FLEX(cls, m) \ C2_DO_NOT_COPY(cls) \ private: \ C2PARAM_MAKE_FRIENDS \ /** \if 0 */ \ template<typename, typename> friend struct _C2FlexHelper; \ public: \ typedef decltype(m) _FlexMemberType; \ /* default constructor with flexCount */ \ inline cls(size_t) : cls() {} \ /* constexpr static _FlexMemberType cls::* flexMember = &cls::m; */ \ typedef typename _C2FlexHelper<_FlexMemberType>::FlexType FlexType; \ static_assert(\ !std::is_void<FlexType>::value, \ "member is not flexible, or a flexible array of a flexible type"); \ enum : uint32_t { FLEX_SIZE = _C2FlexHelper<_FlexMemberType>::FLEX_SIZE }; \ /** \endif */ \ /// @} /** * Global-parameter template. * * Base template to define a global setting/tuning or info based on a structure and * an optional ParamIndex. Global parameters are not tied to a port (input or output). * * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped * structure can be accessed directly, and constructors and potential public methods are also * wrapped. * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped structure * \tparam ParamIndex optional parameter index override. Must be specified for base/reused * structures. */ template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void> struct C2_HIDE C2GlobalParam : public T, public S, public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) private: typedef C2GlobalParam<T, S, ParamIndex> _Type; public: /// Wrapper around base structure's constructor. template<typename ...Args> inline C2GlobalParam(const Args(&... args)) : T(sizeof(_Type), _Type::PARAM_TYPE), S(args...) { } DEFINE_CAST_OPERATORS(_Type) }; /** * Global-parameter template for flexible structures. * * Base template to define a global setting/tuning or info based on a flexible structure and * an optional ParamIndex. Global parameters are not tied to a port (input or output). * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped flexible structure * \tparam ParamIndex optional parameter index override. Must be specified for base/reused * structures. * * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible * structures can be accessed via the m member variable; however, the constructors of the structure * are wrapped directly. (This is because flexible types cannot be subclassed.) */ template<typename T, typename S, int ParamIndex> struct C2_HIDE C2GlobalParam<T, S, ParamIndex, IF_FLEXIBLE(S)> : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> { private: typedef C2GlobalParam<T, S, ParamIndex> _Type; /// Wrapper around base structure's constructor. template<typename ...Args> inline C2GlobalParam(size_t flexCount, const Args(&... args)) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure DEFINE_FLEXIBLE_METHODS(_Type, S) DEFINE_CAST_OPERATORS(_Type) }; /** * Port-parameter template. * * Base template to define a port setting/tuning or info based on a structure and * an optional ParamIndex. Port parameters are tied to a port (input or output), but not to a * specific stream. * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped structure * \tparam ParamIndex optional parameter index override. Must be specified for base/reused * structures. * * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped * structure can be accessed directly, and constructors and potential public methods are also * wrapped. * * There are 3 flavors of port parameters: unspecified, input and output. Parameters with * unspecified port expose a setPort method, and add an initial port parameter to the constructor. */ template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void> struct C2_HIDE C2PortParam : public T, public S, private _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_UNDEFINED> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) private: typedef C2PortParam<T, S, ParamIndex> _Type; public: /// Default constructor. inline C2PortParam() : T(sizeof(_Type), _Type::PARAM_TYPE) { } template<typename ...Args> /// Wrapper around base structure's constructor while specifying port/direction. inline C2PortParam(bool _output, const Args(&... args)) : T(sizeof(_Type), _output ? output::PARAM_TYPE : input::PARAM_TYPE), S(args...) { } /// Set port/direction. inline void setPort(bool output) { C2Param::setPort(output); } DEFINE_CAST_OPERATORS(_Type) /// Specialization for an input port parameter. struct input : public T, public S, public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) /// Wrapper around base structure's constructor. template<typename ...Args> inline input(const Args(&... args)) : T(sizeof(_Type), input::PARAM_TYPE), S(args...) { } DEFINE_CAST_OPERATORS(input) }; /// Specialization for an output port parameter. struct output : public T, public S, public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) /// Wrapper around base structure's constructor. template<typename ...Args> inline output(const Args(&... args)) : T(sizeof(_Type), output::PARAM_TYPE), S(args...) { } DEFINE_CAST_OPERATORS(output) }; }; /** * Port-parameter template for flexible structures. * * Base template to define a port setting/tuning or info based on a flexible structure and * an optional ParamIndex. Port parameters are tied to a port (input or output), but not to a * specific stream. * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped flexible structure * \tparam ParamIndex optional parameter index override. Must be specified for base/reused * structures. * * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible * structures can be accessed via the m member variable; however, the constructors of the structure * are wrapped directly. (This is because flexible types cannot be subclassed.) * * There are 3 flavors of port parameters: unspecified, input and output. Parameters with * unspecified port expose a setPort method, and add an initial port parameter to the constructor. */ template<typename T, typename S, int ParamIndex> struct C2_HIDE C2PortParam<T, S, ParamIndex, IF_FLEXIBLE(S)> : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_UNDEFINED> { private: typedef C2PortParam<T, S, ParamIndex> _Type; /// Default constructor for basic allocation: new(flexCount) P. inline C2PortParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE) { } template<typename ...Args> /// Wrapper around base structure's constructor while also specifying port/direction. inline C2PortParam(size_t flexCount, bool _output, const Args(&... args)) : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE), m(flexCount, args...) { } public: /// Set port/direction. inline void setPort(bool output) { C2Param::setPort(output); } S m; ///< wrapped flexible structure DEFINE_FLEXIBLE_METHODS(_Type, S) DEFINE_CAST_OPERATORS(_Type) /// Specialization for an input port parameter. struct input : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> { private: /// Wrapper around base structure's constructor while also specifying port/direction. template<typename ...Args> inline input(size_t flexCount, const Args(&... args)) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure DEFINE_FLEXIBLE_METHODS(input, S) DEFINE_CAST_OPERATORS(input) }; /// Specialization for an output port parameter. struct output : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> { private: /// Wrapper around base structure's constructor while also specifying port/direction. template<typename ...Args> inline output(size_t flexCount, const Args(&... args)) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure DEFINE_FLEXIBLE_METHODS(output, S) DEFINE_CAST_OPERATORS(output) }; }; /** * Stream-parameter template. * * Base template to define a stream setting/tuning or info based on a structure and * an optional ParamIndex. Stream parameters are tied to a specific stream on a port (input or * output). * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped structure * \tparam ParamIndex optional paramter index override. Must be specified for base/reused * structures. * * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped * structure can be accessed directly, and constructors and potential public methods are also * wrapped. * * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose * a setStream method and an extra initial streamID parameter for the constructor. Moreover, * parameters with unspecified port expose a setPort method, and add an additional initial port * parameter to the constructor. */ template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void> struct C2_HIDE C2StreamParam : public T, public S, private _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) private: typedef C2StreamParam<T, S, ParamIndex> _Type; public: /// Default constructor. Port/direction and stream-ID is undefined. inline C2StreamParam() : T(sizeof(_Type), _Type::PARAM_TYPE) { } /// Wrapper around base structure's constructor while also specifying port/direction and /// stream-ID. template<typename ...Args> inline C2StreamParam(bool _output, unsigned stream, const Args(&... args)) : T(sizeof(_Type), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream), S(args...) { } /// Set port/direction. inline void setPort(bool output) { C2Param::setPort(output); } /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_CAST_OPERATORS(_Type) /// Specialization for an input stream parameter. struct input : public T, public S, public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) /// Default constructor. Stream-ID is undefined. inline input() : T(sizeof(_Type), input::PARAM_TYPE) { } /// Wrapper around base structure's constructor while also specifying stream-ID. template<typename ...Args> inline input(unsigned stream, const Args(&... args)) : T(sizeof(_Type), input::PARAM_TYPE, stream), S(args...) { } /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_CAST_OPERATORS(input) }; /// Specialization for an output stream parameter. struct output : public T, public S, public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> { _C2_CORE_INDEX_OVERRIDE(ParamIndex) /// Default constructor. Stream-ID is undefined. inline output() : T(sizeof(_Type), output::PARAM_TYPE) { } /// Wrapper around base structure's constructor while also specifying stream-ID. template<typename ...Args> inline output(unsigned stream, const Args(&... args)) : T(sizeof(_Type), output::PARAM_TYPE, stream), S(args...) { } /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_CAST_OPERATORS(output) }; }; /** * Stream-parameter template for flexible structures. * * Base template to define a stream setting/tuning or info based on a flexible structure and * an optional ParamIndex. Stream parameters are tied to a specific stream on a port (input or * output). * * \tparam T param type C2Setting, C2Tuning or C2Info * \tparam S wrapped flexible structure * \tparam ParamIndex optional parameter index override. Must be specified for base/reused * structures. * * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible * structures can be accessed via the m member variable; however, the constructors of the structure * are wrapped directly. (This is because flexible types cannot be subclassed.) * * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose * a setStream method and an extra initial streamID parameter for the constructor. Moreover, * parameters with unspecified port expose a setPort method, and add an additional initial port * parameter to the constructor. */ template<typename T, typename S, int ParamIndex> struct C2_HIDE C2StreamParam<T, S, ParamIndex, IF_FLEXIBLE(S)> : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> { private: typedef C2StreamParam<T, S, ParamIndex> _Type; /// Default constructor. Port/direction and stream-ID is undefined. inline C2StreamParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE, 0u) { } /// Wrapper around base structure's constructor while also specifying port/direction and /// stream-ID. template<typename ...Args> inline C2StreamParam(size_t flexCount, bool _output, unsigned stream, const Args(&... args)) : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure /// Set port/direction. inline void setPort(bool output) { C2Param::setPort(output); } /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_FLEXIBLE_METHODS(_Type, S) DEFINE_CAST_OPERATORS(_Type) /// Specialization for an input stream parameter. struct input : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> { private: /// Default constructor. Stream-ID is undefined. inline input(size_t flexCount) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE) { } /// Wrapper around base structure's constructor while also specifying stream-ID. template<typename ...Args> inline input(size_t flexCount, unsigned stream, const Args(&... args)) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE, stream), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_FLEXIBLE_METHODS(input, S) DEFINE_CAST_OPERATORS(input) }; /// Specialization for an output stream parameter. struct output : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> { private: /// Default constructor. Stream-ID is undefined. inline output(size_t flexCount) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE) { } /// Wrapper around base structure's constructor while also specifying stream-ID. template<typename ...Args> inline output(size_t flexCount, unsigned stream, const Args(&... args)) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE, stream), m(flexCount, args...) { } public: S m; ///< wrapped flexible structure /// Set stream-id. \retval true if the stream-id was successfully set. inline bool setStream(unsigned stream) { return C2Param::setStream(stream); } DEFINE_FLEXIBLE_METHODS(output, S) DEFINE_CAST_OPERATORS(output) }; }; /* ======================== SIMPLE VALUE PARAMETERS ======================== */ /** * \ingroup internal * A structure template encapsulating a single element with default constructors and no core-index. */ template<typename T> struct C2SimpleValueStruct { T value; ///< simple value of the structure // Default constructor. inline C2SimpleValueStruct() = default; // Constructor with an initial value. inline C2SimpleValueStruct(T value) : value(value) {} DEFINE_BASE_C2STRUCT(SimpleValue) }; // TODO: move this and next to some generic place /** * Interface to a block of (mapped) memory containing an array of some type (T). */ template<typename T> struct C2MemoryBlock { /// \returns the number of elements in this block. virtual size_t size() const = 0; /// \returns a const pointer to the start of this block. Care must be taken to not read outside /// the block. virtual const T *data() const = 0; // TODO: should this be friend access only in some C2Memory module? /// \returns a pointer to the start of this block. Care must be taken to not read or write /// outside the block. inline T *data() { return const_cast<T*>(const_cast<const C2MemoryBlock*>(this)->data()); } protected: // TODO: for now it should never be deleted as C2MemoryBlock virtual ~C2MemoryBlock() = default; }; /** * Interface to a block of memory containing a constant (constexpr) array of some type (T). */ template<typename T> struct C2ConstMemoryBlock : public C2MemoryBlock<T> { virtual const T * data() const { return _mData; } virtual size_t size() const { return _mSize; } /// Constructor. template<unsigned N> inline constexpr C2ConstMemoryBlock(const T(&init)[N]) : _mData(init), _mSize(N) {} private: const T *_mData; const size_t _mSize; }; /// \addtogroup internal /// @{ /// Helper class to initialize flexible arrays with various initalizers. struct _C2ValueArrayHelper { // char[]-s are used as null terminated strings, so the last element is never inited. /// Initialize a flexible array using a constexpr memory block. template<typename T> static void init(T(&array)[], size_t arrayLen, const C2MemoryBlock<T> &block) { // reserve last element for terminal 0 for strings if (arrayLen && std::is_same<T, char>::value) { --arrayLen; } if (block.data()) { memcpy(array, block.data(), std::min(arrayLen, block.size()) * sizeof(T)); } } /// Initialize a flexible array using an initializer list. template<typename T> static void init(T(&array)[], size_t arrayLen, const std::initializer_list<T> &init) { size_t ix = 0; // reserve last element for terminal 0 for strings if (arrayLen && std::is_same<T, char>::value) { --arrayLen; } for (const T &item : init) { if (ix == arrayLen) { break; } array[ix++] = item; } } /// Initialize a flexible array using a vector. template<typename T> static void init(T(&array)[], size_t arrayLen, const std::vector<T> &init) { size_t ix = 0; // reserve last element for terminal 0 for strings if (arrayLen && std::is_same<T, char>::value) { --arrayLen; } for (const T &item : init) { if (ix == arrayLen) { break; } array[ix++] = item; } } /// Initialize a flexible array using another flexible array. template<typename T, unsigned N> static void init(T(&array)[], size_t arrayLen, const T(&str)[N]) { // reserve last element for terminal 0 for strings if (arrayLen && std::is_same<T, char>::value) { --arrayLen; } if (arrayLen) { memcpy(array, str, std::min(arrayLen, (size_t)N) * sizeof(T)); } } }; /** * Specialization for a flexible blob and string arrays. A structure template encapsulating a single * flexible array member with default flexible constructors and no core-index. This type cannot be * constructed on its own as it's size is 0. * * \internal This is different from C2SimpleArrayStruct<T[]> simply because its member has the name * as value to reflect this is a single value. */ template<typename T> struct C2SimpleValueStruct<T[]> { static_assert(std::is_same<T, char>::value || std::is_same<T, uint8_t>::value, "C2SimpleValueStruct<T[]> is only for BLOB or STRING"); T value[]; inline C2SimpleValueStruct() = default; DEFINE_BASE_C2STRUCT(SimpleValue) FLEX(C2SimpleValueStruct, value) private: inline C2SimpleValueStruct(size_t flexCount, const C2MemoryBlock<T> &block) { _C2ValueArrayHelper::init(value, flexCount, block); } inline C2SimpleValueStruct(size_t flexCount, const std::initializer_list<T> &init) { _C2ValueArrayHelper::init(value, flexCount, init); } inline C2SimpleValueStruct(size_t flexCount, const std::vector<T> &init) { _C2ValueArrayHelper::init(value, flexCount, init); } template<unsigned N> inline C2SimpleValueStruct(size_t flexCount, const T(&init)[N]) { _C2ValueArrayHelper::init(value, flexCount, init); } }; /// @} /** * A structure template encapsulating a single flexible array element of a specific type (T) with * default constructors and no core-index. This type cannot be constructed on its own as it's size * is 0. Instead, it is meant to be used as a parameter, e.g. * * typedef C2StreamParam<C2Info, C2SimpleArrayStruct<C2MyFancyStruct>, * kParamIndexMyFancyArrayStreamParam> C2MyFancyArrayStreamInfo; */ template<typename T> struct C2SimpleArrayStruct { static_assert(!std::is_same<T, char>::value && !std::is_same<T, uint8_t>::value, "use C2SimpleValueStruct<T[]> is for BLOB or STRING"); T values[]; ///< array member /// Default constructor inline C2SimpleArrayStruct() = default; DEFINE_BASE_FLEX_C2STRUCT(SimpleArray, values) //FLEX(C2SimpleArrayStruct, values) private: /// Construct from a C2MemoryBlock. /// Used only by the flexible parameter allocators (AllocUnique & AllocShared). inline C2SimpleArrayStruct(size_t flexCount, const C2MemoryBlock<T> &block) { _C2ValueArrayHelper::init(values, flexCount, block); } /// Construct from an initializer list. /// Used only by the flexible parameter allocators (AllocUnique & AllocShared). inline C2SimpleArrayStruct(size_t flexCount, const std::initializer_list<T> &init) { _C2ValueArrayHelper::init(values, flexCount, init); } /// Construct from an vector. /// Used only by the flexible parameter allocators (AllocUnique & AllocShared). inline C2SimpleArrayStruct(size_t flexCount, const std::vector<T> &init) { _C2ValueArrayHelper::init(values, flexCount, init); } /// Construct from another flexible array. /// Used only by the flexible parameter allocators (AllocUnique & AllocShared). template<unsigned N> inline C2SimpleArrayStruct(size_t flexCount, const T(&init)[N]) { _C2ValueArrayHelper::init(values, flexCount, init); } }; /** * \addtogroup simplevalue Simple value and array structures. * @{ * * Simple value structures. * * Structures containing a single simple value. These can be reused to easily define simple * parameters of various types: * * typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexMyIntegerPortParam> * C2MyIntegerPortParamTuning; * * They contain a single member (value or values) that is described as "value" or "values". * * These structures don't define a core index, and as such, they cannot be used in structure * declarations. Use type[] instead, such as int32_t field[]. */ /// A 32-bit signed integer parameter in value, described as "value" typedef C2SimpleValueStruct<int32_t> C2Int32Value; /// A 32-bit signed integer array parameter in values, described as "values" typedef C2SimpleArrayStruct<int32_t> C2Int32Array; /// A 32-bit unsigned integer parameter in value, described as "value" typedef C2SimpleValueStruct<uint32_t> C2Uint32Value; /// A 32-bit unsigned integer array parameter in values, described as "values" typedef C2SimpleArrayStruct<uint32_t> C2Uint32Array; /// A 64-bit signed integer parameter in value, described as "value" typedef C2SimpleValueStruct<int64_t> C2Int64Value; /// A 64-bit signed integer array parameter in values, described as "values" typedef C2SimpleArrayStruct<int64_t> C2Int64Array; /// A 64-bit unsigned integer parameter in value, described as "value" typedef C2SimpleValueStruct<uint64_t> C2Uint64Value; /// A 64-bit unsigned integer array parameter in values, described as "values" typedef C2SimpleArrayStruct<uint64_t> C2Uint64Array; /// A float parameter in value, described as "value" typedef C2SimpleValueStruct<float> C2FloatValue; /// A float array parameter in values, described as "values" typedef C2SimpleArrayStruct<float> C2FloatArray; /// A blob flexible parameter in value, described as "value" typedef C2SimpleValueStruct<uint8_t[]> C2BlobValue; /// A string flexible parameter in value, described as "value" typedef C2SimpleValueStruct<char[]> C2StringValue; template<typename T> const std::vector<C2FieldDescriptor> C2SimpleValueStruct<T>::FieldList() { return { DESCRIBE_C2FIELD(value, "value") }; } template<typename T> const std::vector<C2FieldDescriptor> C2SimpleValueStruct<T[]>::FieldList() { return { DESCRIBE_C2FIELD(value, "value") }; } template<typename T> const std::vector<C2FieldDescriptor> C2SimpleArrayStruct<T>::FieldList() { return { DESCRIBE_C2FIELD(values, "values") }; } /// @} /// @} #endif // C2PARAM_DEF_H_