Files
openvehicle-api/export/support/param_impl.inl
2026-03-27 14:12:49 +01:00

732 lines
26 KiB
C++

/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef PARAM_IMPL_INL
#define PARAM_IMPL_INL
#ifndef PARAM_IMPL_H
#include "param_impl.h"
#endif //! defined PARAM_IMPL_H
#include <regex>
#include <set>
/**
* @brief Software Defined Vehicle framework.
*/
namespace sdv
{
template <typename TVar, typename TDefaultType>
inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssUnit,
const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam)
{
EParamType eTypeLocal = EParamType::boolean_param;
if constexpr (std::is_same_v<TVar, bool>)
eTypeLocal = EParamType::boolean_param;
else if constexpr (std::is_arithmetic_v<TVar>)
eTypeLocal = EParamType::number_param; // No number limitation
else if constexpr (TypeIsString<TVar>())
eTypeLocal = EParamType::string_param; // No pattern
else if constexpr (std::is_enum_v<TVar>)
eTypeLocal = EParamType::enum_param; // No enumerator labels
else
static_assert(sdv::internal::always_false_v<TVar>, "The parameter type is not supported!");
any_t anyDefaultValLocal;
if constexpr (std::is_enum_v<TVar>)
{
static_assert(std::is_same_v<std::remove_reference_t<std::remove_const_t<TVar>>, TDefaultType>,
"The enum types of the value and the default value must be identical!");
anyDefaultValLocal = static_cast<std::underlying_type_t<TVar>>(tDefaultVal);
}
else
anyDefaultValLocal = tDefaultVal;
uint32_t uiFlagsLocal = uiFlagsParam;
if (TypeIsReadOnly<TVar>())
uiFlagsLocal |= static_cast<uint32_t>(EParamFlags::read_only);
if (uiFlagsLocal & static_cast<uint32_t>(EParamFlags::read_only))
uiFlagsLocal &= ~static_cast<uint32_t>(EParamFlags::locked); // Readonly cannot write
Init(eTypeLocal, rssName, anyDefaultValLocal, rssUnit, rssCategory, rssDescription, uiFlagsLocal);
if constexpr (std::is_enum_v<TVar>)
uExtInfo.sEnumInfo.seqLabels = internal::GetLabelMapHelper().GetLabelMap<TVar>();
}
template <typename TVar, typename TDefaultType>
inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const any_t& ranyLimitLow,
bool bIncludeLow, const any_t& ranyLimitHigh, bool bIncludeHigh, const std::string& rssUnit, const std::string& rssCategory,
const std::string& rssDescription, uint32_t uiFlagsParam)
{
static_assert(std::is_arithmetic_v<TVar> && !std::is_same_v<TVar, bool>,
"The construct function for numeric types expects the type to be numeric.");
uint32_t uiFlagsLocal = uiFlagsParam;
if (TypeIsReadOnly<TVar>())
uiFlagsLocal |= static_cast<uint32_t>(EParamFlags::read_only);
if (uiFlagsLocal & static_cast<uint32_t>(EParamFlags::read_only))
uiFlagsLocal &= ~static_cast<uint32_t>(EParamFlags::locked); // Readonly cannot write
Init(EParamType::number_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal);
uExtInfo.sNumberInfo.anyLowerLimit = ranyLimitLow;
uExtInfo.sNumberInfo.bIncludeLowerLinit = bIncludeLow;
uExtInfo.sNumberInfo.anyUpperLimit = ranyLimitHigh;
uExtInfo.sNumberInfo.bIncludeUpperLimit = bIncludeHigh;
}
template <typename TVar, typename TDefaultType>
inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssPattern,
const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam)
{
static_assert(TypeIsString<TVar>(), "The construct function for string types expects the type to be a string.");
uint32_t uiFlagsLocal = uiFlagsParam;
if (TypeIsReadOnly<TVar>())
uiFlagsLocal |= static_cast<uint32_t>(EParamFlags::read_only);
if (uiFlagsLocal & static_cast<uint32_t>(EParamFlags::read_only))
uiFlagsLocal &= ~static_cast<uint32_t>(EParamFlags::locked); // Readonly cannot write
Init(EParamType::string_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal);
uExtInfo.sStringInfo.ssPattern = rssPattern;
}
template <typename TVar, typename TDefaultType>
inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal,
const sequence<SLabelInfo::SLabel>& rseqLabels, const std::string& rssCategory, const std::string& rssDescription,
uint32_t uiFlagsParam)
{
static_assert(std::is_enum_v<TVar> || (std::is_integral_v<TVar> && !std::is_same_v<TVar, bool>),
"The construct function for enumerators and bitmasks expects the type to be an enum or an integral type.");
uint32_t uiFlagsLocal = uiFlagsParam;
if (TypeIsReadOnly<TVar>())
uiFlagsLocal |= static_cast<uint32_t>(EParamFlags::read_only);
if (uiFlagsLocal & static_cast<uint32_t>(EParamFlags::read_only))
uiFlagsLocal &= ~static_cast<uint32_t>(EParamFlags::locked); // Readonly cannot write
if constexpr (std::is_enum_v<TVar>)
{
Init(EParamType::enum_param, rssName, static_cast<std::underlying_type_t<TVar>>(tDefaultVal), "",
rssCategory, rssDescription, uiFlagsLocal);
uExtInfo.sEnumInfo.seqLabels = rseqLabels;
}
else
{
Init(EParamType::bitmask_param, rssName, tDefaultVal, "", rssCategory, rssDescription, uiFlagsLocal);
uExtInfo.sBitmaskInfo.seqLabels = rseqLabels;
}
}
inline CSdvParamInfo::CSdvParamInfo(const SParamInfo& rInfo) : SParamInfo(rInfo)
{}
inline CSdvParamInfo::CSdvParamInfo(SParamInfo&& rInfo) : SParamInfo(std::move(rInfo))
{}
inline CSdvParamInfo& CSdvParamInfo::operator=(const SParamInfo& rsInfo)
{
static_cast<SParamInfo&>(*this) = rsInfo;
return *this;
}
inline CSdvParamInfo& CSdvParamInfo::operator=(SParamInfo&& rsInfo)
{
static_cast<SParamInfo&>(*this) = std::move(rsInfo);
return *this;
}
inline SParamInfo CSdvParamInfo::InfoStruct() const
{
return *this;
}
inline const u8string& CSdvParamInfo::Name() const
{
return SParamInfo::ssName;
}
inline u8string CSdvParamInfo::Path() const
{
sdv::u8string ssPath;
if (!SParamInfo::ssGroup.empty())
ssPath += ssGroup + ".";
ssPath += ssName;
return ssPath;
}
inline const any_t& CSdvParamInfo::DefaultVal() const
{
return SParamInfo::anyDefaultVal;
}
inline const u8string& CSdvParamInfo::Group() const
{
return SParamInfo::ssGroup;
}
inline const u8string& CSdvParamInfo::Description() const
{
return SParamInfo::ssDescription;
}
inline const u8string& CSdvParamInfo::Unit() const
{
return SParamInfo::ssUnit;
}
inline uint32_t CSdvParamInfo::Flags() const
{
return uiFlags;
}
inline bool CSdvParamInfo::ReadOnly() const
{
return (uiFlags & static_cast<uint32_t>(EParamFlags::read_only)) ? true : false;
}
inline bool CSdvParamInfo::Temporary() const
{
return (uiFlags & static_cast<uint32_t>(EParamFlags::temporary)) ? true : false;
}
inline bool CSdvParamInfo::Dirty() const
{
return (uiFlags & static_cast<uint32_t>(EParamFlags::dirty)) ? true : false;
}
inline bool CSdvParamInfo::Locked() const
{
return (uiFlags & static_cast<uint32_t>(EParamFlags::locked)) ? true : false;
}
inline bool CSdvParamInfo::Boolean() const
{
return get_switch() == EParamType::boolean_param;
}
inline bool CSdvParamInfo::Numeric() const
{
return get_switch() == EParamType::number_param;
}
inline bool CSdvParamInfo::String() const
{
return get_switch() == EParamType::string_param;
}
inline bool CSdvParamInfo::Enum() const
{
return get_switch() == EParamType::enum_param;
}
inline bool CSdvParamInfo::Bitmask() const
{
return get_switch() == EParamType::bitmask_param;
}
inline std::pair<any_t, bool> CSdvParamInfo::NumericLimitLow() const
{
if (!Numeric()) return {};
return std::make_pair(uExtInfo.sNumberInfo.anyLowerLimit, uExtInfo.sNumberInfo.bIncludeLowerLinit);
}
inline std::pair<any_t, bool> CSdvParamInfo::NumericLimitHigh() const
{
if (!Numeric()) return {};
return std::make_pair(uExtInfo.sNumberInfo.anyUpperLimit, uExtInfo.sNumberInfo.bIncludeUpperLimit);
}
inline std::string CSdvParamInfo::StringPattern() const
{
if (!String()) return {};
return uExtInfo.sStringInfo.ssPattern;
}
inline sequence<SLabelInfo::SLabel> CSdvParamInfo::EnumBitmaskLabels() const
{
if (Enum())
return uExtInfo.sEnumInfo.seqLabels;
else if (Bitmask())
return uExtInfo.sBitmaskInfo.seqLabels;
else
return {};
}
inline void CSdvParamInfo::Init(EParamType eTypeParam, const std::string& rssName, const any_t& ranyDefaultVal,
const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam)
{
switch_to(eTypeParam);
uiFlags = uiFlagsParam;
ssName = rssName;
anyDefaultVal = ranyDefaultVal;
ssUnit = rssUnit;
ssGroup = rssCategory;
ssDescription = rssDescription;
}
namespace internal
{
template <typename... TInfoConstruct>
inline CParamGuardian::CParamGuardian(bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) :
CSdvParamInfo(tConstruct...), m_bLockable(bLockable), m_bAutoDirty(bAutoDirty)
{}
inline SParamInfo CParamGuardian::InfoStruct() const
{
sdv::SParamInfo sInfo = CSdvParamInfo::InfoStruct();
sInfo.uiFlags = Flags();
return sInfo;
}
inline uint32_t CParamGuardian::Flags() const
{
uint32_t uiFlagsTemp = CSdvParamInfo::Flags() & ~static_cast<uint32_t>(EParamFlags::state_mask);
if (m_bLocked)
uiFlagsTemp |= static_cast<uint32_t>(EParamFlags::locked);
if (m_bDirty)
uiFlagsTemp |= static_cast<uint32_t>(EParamFlags::dirty);
return uiFlagsTemp;
}
inline bool CParamGuardian::UpdateDirty(const sdv::any_t& ranyValue)
{
if (!m_bAutoDirty) return false;
bool bDirty = m_bDirty;
m_bDirty |= ranyValue != m_anyStored;
m_anyStored = ranyValue;
return m_bDirty != bDirty;
}
inline bool CParamGuardian::SetDirty()
{
bool bDirty = m_bDirty;
m_bDirty = true;
return m_bDirty != bDirty;
}
inline bool CParamGuardian::ResetDirty()
{
bool bDirty = m_bDirty;
m_bDirty = false;
return m_bDirty != bDirty;
}
inline bool CParamGuardian::Dirty() const
{
return m_bDirty;
}
inline bool CParamGuardian::Lockable() const
{
return m_bLockable;
}
inline bool CParamGuardian::Lock()
{
if (!m_bLockable)
return false;
bool bLocked = m_bLocked;
m_bLocked = true;
return m_bLocked != bLocked;
}
inline bool CParamGuardian::Unlock()
{
if (!m_bLockable)
return false;
bool bLocked = m_bLocked;
m_bLocked = false;
return m_bLocked != bLocked;
}
inline bool CParamGuardian::Locked() const
{
return m_bLocked;
}
inline bool CParamGuardian::CheckRestrictions(const any_t& ranyValue)
{
if (Numeric())
{
// Check for lower limit
auto prLimitLow = NumericLimitLow();
if (!prLimitLow.first.empty())
{
if (prLimitLow.second)
{
if (ranyValue < prLimitLow.first) return false;
} else
{
if (ranyValue <= prLimitLow.first) return false;
}
}
// Check for higher limit
auto prLimitHigh = NumericLimitHigh();
if (!prLimitHigh.first.empty())
{
if (prLimitHigh.second)
{
if (ranyValue > prLimitHigh.first) return false;
}
else
{
if (ranyValue >= prLimitHigh.first) return false;
}
}
}
else if (String())
{
// Check for compatibility to the regular expression pattern.
if (!StringPattern().empty())
{
std::regex regexPattern(StringPattern());
if (!std::regex_match(static_cast<std::string>(ranyValue), regexPattern)) return false;
}
}
else if (Enum())
{
// Check for occurrence in the label sequence
auto seqLabels = EnumBitmaskLabels();
if (std::find_if(seqLabels.begin(), seqLabels.end(), [&](const auto& rsLabel)
{ return ranyValue == rsLabel.anyValue; }) == seqLabels.end()) return false;
}
else if (Bitmask())
{
// Check for the occurence of each set bit in the label sequence
auto seqLabels = EnumBitmaskLabels();
uint64_t uiBitmask = ranyValue;
for (const auto& rsLabel : seqLabels)
uiBitmask &= ~static_cast<uint64_t>(rsLabel.anyValue);
if (uiBitmask) return false;
}
// All okay; restrictions were checked
return true;
}
template <typename TVar>
template <typename... TInfoConstruct>
inline CParamValue<TVar>::CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) :
CParamGuardian(bLockable, bAutoDirty, rtVar, tConstruct...), m_rtVar(rtVar)
{
// Assign the default value
if constexpr (!CSdvParamInfo::TypeIsReadOnly<TVar>())
{
m_rtVar = DefaultVal().get<TVar>();
UpdateDirty(DefaultVal());
ResetDirty();
}
}
template <typename TVar>
inline bool CParamValue<TVar>::Set(const any_t& ranyValue)
{
// Assign the value
if constexpr (!CSdvParamInfo::TypeIsReadOnly<TVar>())
{
if (!Locked() && !ReadOnly() && CheckRestrictions(ranyValue))
{
m_rtVar = ranyValue.get<TVar>();
UpdateDirty(ranyValue);
return true;
}
}
return false;
}
template <typename TVar>
inline any_t CParamValue<TVar>::Get() const
{
// Remarks: in case of an enum, an explicit construction is needed.
return sdv::any_t(m_rtVar);
}
} // namespace internal
inline std::shared_ptr<internal::CParamGuardian> CSdvParamMap::FindParamObject(const sdv::u8string& rssPath)
{
// Iterate through the registrations
for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath);
if (ptrParameter) return ptrParameter;
}
// Parameter entry
if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath)
return rsRegistration.Parameter();
}
return {};
}
inline std::shared_ptr<const internal::CParamGuardian> CSdvParamMap::FindParamObject(const sdv::u8string& rssPath) const
{
// Iterate through the registrations
for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath);
if (ptrParameter)
return ptrParameter;
}
// Parameter entry
if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath)
return rsRegistration.Parameter();
}
return {};
}
inline sequence<u8string> CSdvParamMap::GetParamPaths() const
{
// Split path function (splits the group from the parameter names)
auto fnSplitPath = [](const sdv::u8string& rssPath) -> std::pair<sdv::u8string, sdv::u8string>
{
size_t nPos = rssPath.find_last_of('.');
if (nPos == sdv::u8string::npos)
return std::make_pair("", rssPath);
return std::make_pair(rssPath.substr(0, nPos), rssPath.substr(nPos + 1));
};
std::set<u8string> setOneTime;
sequence<u8string> seqPaths;
for (const auto& rsRegistration : m_vecParamMapRegistration)
{
// Add a path to the sequence.
auto fnAddPath = [&](const u8string& rssPath)
{
// Check if already present in the list
if (setOneTime.find(rssPath) != setOneTime.end()) return;
setOneTime.insert(rssPath);
// Sort the paths per group, so all paths belonging to one group is returned together.
// Find thee first location where the group is larger than the one of this path.
auto prPath = fnSplitPath(rssPath);
auto itPos = seqPaths.begin();
while (itPos != seqPaths.end())
{
auto prStoredPath = fnSplitPath(*itPos);
if (prPath.first.compare(prStoredPath.first) < 0) break;
++itPos;
}
// Insert into the sequence
seqPaths.insert(itPos, rssPath);
};
// Add the chained member to the list
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
auto seqNamesMemberMap = rsRegistration.MemberMap()->Access()->GetParamPaths();
for (const auto& rssPath : seqNamesMemberMap)
fnAddPath(rssPath);
}
// Add the parameter to the list
if (rsRegistration.Parameter())
fnAddPath(rsRegistration.Parameter()->Path());
}
return seqPaths;
}
inline any_t CSdvParamMap::GetParam(const u8string& rssPath) const
{
auto ptrParam = FindParamObject(rssPath);
if (!ptrParam) return {};
return ptrParam->Get();
}
inline bool CSdvParamMap::SetParam(const u8string& rssPath, any_t ranyValue)
{
auto ptrParam = FindParamObject(rssPath);
if (!ptrParam) return false;
return ptrParam->Set(ranyValue);
}
inline SParamInfo CSdvParamMap::GetParamInfo(const u8string& rssPath) const
{
auto ptrParamInfo = FindParamObject(rssPath);
if (!ptrParamInfo) return {};
return ptrParamInfo.get()->InfoStruct();
}
inline bool CSdvParamMap::IsParamDirty(const u8string& rssPath) const
{
auto ptrParam = FindParamObject(rssPath);
if (!ptrParam) return false;
return ptrParam->Dirty();
}
inline void CSdvParamMap::SetParamDirtyFlag(const std::string& rssPath)
{
auto ptrParam = FindParamObject(rssPath);
if (!ptrParam) return;
ptrParam->SetDirty();
}
inline void CSdvParamMap::ResetParamDirtyFlag(const u8string& rssPath)
{
auto ptrParam = FindParamObject(rssPath);
if (!ptrParam) return;
ptrParam->ResetDirty();
}
inline bool CSdvParamMap::IsParamMapDirty() const
{
std::set<u8string> setOneTime;
for (const auto& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
if (rsRegistration.MemberMap()->Access()->IsParamMapDirty())
return true;
continue;
}
// Parameter entry
if (rsRegistration.Parameter())
{
// Check if already present in the list
if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end())
continue;
setOneTime.insert(rsRegistration.Parameter()->Name());
// Check for dirtiness
if (rsRegistration.Parameter()->Dirty())
return true;
}
}
return false;
}
inline void CSdvParamMap::ResetParamMapDirtyFlags()
{
std::set<u8string> setOneTime;
for (const auto& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
rsRegistration.MemberMap()->Access()->ResetParamMapDirtyFlags();
continue;
}
// Parameter entry
if (rsRegistration.Parameter())
{
// Check if already present in the list
if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end())
continue;
setOneTime.insert(rsRegistration.Parameter()->Name());
// Reset dirtiness
rsRegistration.Parameter()->ResetDirty();
}
}
}
inline void CSdvParamMap::LockParamMap()
{
for (const auto& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
rsRegistration.MemberMap()->Access()->LockParamMap();
continue;
}
// Parameter entry
if (rsRegistration.Parameter())
{
// Lock
rsRegistration.Parameter()->Lock();
}
}
}
inline void CSdvParamMap::UnlockParamMap()
{
for (const auto& rsRegistration : m_vecParamMapRegistration)
{
// Member parameter map entry
if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access())
{
rsRegistration.MemberMap()->Access()->UnlockParamMap();
continue;
}
// Parameter entry
if (rsRegistration.Parameter())
{
// Lock
rsRegistration.Parameter()->Unlock();
}
}
}
template <typename TVar, typename... TConstruct>
inline std::shared_ptr<CSdvParamInfo> CSdvParamMap::RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty,
TConstruct... tConstruct)
{
auto ptrParam = std::make_shared<internal::CParamValue<TVar>>(rtVar, bLockable, bAutoDirty, tConstruct...);
m_vecParamMapRegistration.push_back(SParamRegistration(ptrParam));
return ptrParam;
}
inline void CSdvParamMap::RegisterMemberMap(const std::shared_ptr<internal::SMemberMapAccess>& rptrMember)
{
m_vecParamMapRegistration.push_back(SParamRegistration(rptrMember));
}
inline void CSdvParamMap::InitParamMap()
{
// Nothing to do here.
}
inline bool CSdvParamMap::BuildNecessary() const
{
return m_vecParamMapRegistration.empty();
}
template <typename TType>
constexpr bool CSdvParamInfo::TypeIsString()
{
using TType2 = std::remove_cv_t<TType>;
using TRoot = std::remove_cv_t<std::remove_pointer_t<std::remove_extent_t<TType2>>>;
constexpr bool bArray = std::is_array_v<TType2>;
constexpr bool bPointer = std::is_pointer_v<TType2>;
constexpr bool bCharacter = std::is_same_v<TRoot, char> || std::is_same_v<TRoot, char16_t> ||
std::is_same_v<TRoot, char32_t> || std::is_same_v<TRoot, wchar_t>;
constexpr bool bStdString = std::is_same_v<TType2, std:: string> || std::is_same_v<TType2, std::u16string> ||
std::is_same_v<TType2, std::u32string> || std::is_same_v<TType2, std::wstring>;
constexpr bool bSdvString = std::is_same_v<TType2, string> || std::is_same_v<TType2, u8string> ||
std::is_same_v<TType2, u16string> || std::is_same_v<TType2, u32string> || std::is_same_v<TType2, wstring>;
constexpr bool bStdPath = std::is_same_v<TType2, std::filesystem::path>;
return bStdString || bSdvString || (bArray && bCharacter) || (bPointer && bCharacter) || bStdPath;
}
template <typename TType>
constexpr bool CSdvParamInfo::TypeIsReadOnly()
{
using TType2 = std::remove_cv_t<TType>;
constexpr bool bArray = std::is_array_v<TType2>;
constexpr bool bPointer = std::is_pointer_v<TType2>;
return bArray || bPointer || std::is_const_v<TType>;
}
}; // namespace sdv
#endif // !defined PARAM_IMPL_INL