PLCnext API Documentation 25.0.2.69
EnumFlagsDictionary.hxx
1
2//
3// Copyright Phoenix Contact GmbH & Co. KG
4//
6#pragma once
7
8#ifndef ARP_USE_ARP_SYSTEM_CORE
9
10#include "Arp/Base/Core/EnumFlagsDictionary.hxx"
11
12#else
13
14#include "Arp/System/Core/Arp.h"
15#include "Arp/System/Core/EnumDictionaryBase.hxx"
16#include "Arp/System/Core/TypeName.hxx"
17#include <iomanip>
18
19namespace Arp
20{
21
28template<class T, char Delimiter = '|'>
29class EnumFlagsDictionary : public EnumDictionaryBase<T>
30{
31protected: // usings
32 using Base = EnumDictionaryBase<T>;
33 using U = typename Base::U;
34 using InitializerList = typename Base::InitializerList;
35
36public: // construction
39 EnumFlagsDictionary(const InitializerList& fields);
40
44 EnumFlagsDictionary(T flagsMask, const InitializerList& fields);
45
50 EnumFlagsDictionary(T flagsMask, const char* invalidValueString, const InitializerList& fields);
51
55 EnumFlagsDictionary(U flagsMask, const InitializerList& fields);
56
61 EnumFlagsDictionary(U flagsMask, const char* invalidValueString, const InitializerList& fields);
62
63public: // overlapping operations from base class EnumDictionary<T> (used by stream operators of enums)
69 std::ostream& WriteEnumString(std::ostream& os, T value)const;
70
75 std::istream& ReadEnumValue(std::istream& is, T& value)const;
76
77public: // flags operation
82 String GetEnumString(T value)const;
83
88 bool TryGetEnumString(T value, String& result)const;
89
94 T GetEnumValue(const String& fieldName)const;
95
100 bool TryGetEnumValue(const String& fieldName, T& value)const;
101
102private: // methods
103 bool IsFlag(T value)const;
104 bool TryGetCombinedEnumString(T value, String& result)const;
105 bool TryParseCombinedEnumValue(const String& input, T& value)const;
106 bool TryFormatFlags(T value, String& result)const;
107
108private: // fields
109 T flagsMask = static_cast<T>(std::numeric_limits<U>::max());
110 T valueMask = static_cast<T>(0);
111};
112
114// inline methods of class EnumFlagsDictionary<T>
115template<class T, char Delimiter>
117 : Base("", fields)
118{
119}
120
121template<class T, char Delimiter>
122EnumFlagsDictionary<T, Delimiter>::EnumFlagsDictionary(T flagsMask, const InitializerList& fields)
123 : Base("", fields)
124 , flagsMask(flagsMask)
125 , valueMask(~flagsMask)
126{
127}
128
129template<class T, char Delimiter>
130EnumFlagsDictionary<T, Delimiter>::EnumFlagsDictionary(T flagsMask, const char* invalidValueString, const InitializerList& fields)
131 : Base(invalidValueString, fields)
132 , flagsMask(flagsMask)
133 , valueMask(~flagsMask)
134{
135}
136
137template<class T, char Delimiter>
138EnumFlagsDictionary<T, Delimiter>::EnumFlagsDictionary(U flagsMask, const InitializerList& fields)
139 : Base("", fields)
140 , flagsMask(static_cast<T>(flagsMask))
141 , valueMask(static_cast<T>(~flagsMask))
142{
143}
144
145template<class T, char Delimiter>
146EnumFlagsDictionary<T, Delimiter>::EnumFlagsDictionary(U flagsMask, const char* invalidValueString, const InitializerList& fields)
147 : Base(invalidValueString, fields)
148 , flagsMask(static_cast<T>(flagsMask))
149 , valueMask(static_cast<T>(~flagsMask))
150{
151}
152
153template<class T, char Delimiter>
154std::ostream& EnumFlagsDictionary<T, Delimiter>::WriteEnumString(std::ostream& os, T value)const
155{
156 String enumString;
157 if (this->TryGetEnumString(value, enumString))
158 {
159 os << enumString;
160 }
161 else
162 {
163 if (this->invalidValueString.IsEmpty())
164 {
165 os << String::Format("0x{:02X}", static_cast<U>(value));
166 }
167 else
168 {
169 os << this->invalidValueString;
170 }
171 os.setstate(std::ios::failbit); // inform caller that entry was not
172 }
173 return os;
174}
175
176template<class T, char Delimiter>
177inline std::istream& EnumFlagsDictionary<T, Delimiter>::ReadEnumValue(std::istream& is, T& value)const
178{
179 std::string s;
180 std::getline(is, s);
181 if (!this->TryParseCombinedEnumValue(s, value))
182 {
183 is.setstate(std::ios::failbit);
184 }
185 return is;
186}
187
188template<class T, char Delimiter>
189inline String EnumFlagsDictionary<T, Delimiter>::GetEnumString(T value)const
190{
191 String result;
192 if (!this->TryGetCombinedEnumString(value, result))
193 {
194 throw Exception("Could not find enum string of enum '{}' for value '{}'.", TypeName<T>(), static_cast<U>(value));
195 }
196 // else
197 return result;
198}
199
200template<class T, char Delimiter>
201bool EnumFlagsDictionary<T, Delimiter>::TryGetEnumString(T value, String& result)const
202{
203 return this->TryGetCombinedEnumString(value, result);
204}
205
206template<class T, char Delimiter>
207T EnumFlagsDictionary<T, Delimiter>::GetEnumValue(const String& fieldName)const
208{
209 T result;
210 if (!this->TryGetEnumValue(fieldName, result))
211 {
212 throw Exception("Could not find enum value of enum '{}' for string '{}'.", TypeName<T>(), fieldName);
213 }
214 return result;
215}
216
217template<class T, char Delimiter>
218bool EnumFlagsDictionary<T, Delimiter>::TryGetEnumValue(const String& fieldName, T& value)const
219{
220 return this->TryParseCombinedEnumValue(fieldName, value);
221}
222
223template<class T, char Delimiter>
224inline bool EnumFlagsDictionary<T, Delimiter>::IsFlag(T value)const
225{
226 return (value & this->flagsMask) == value;
227}
228
229template<class T, char Delimiter>
230inline bool EnumFlagsDictionary<T, Delimiter>::TryGetCombinedEnumString(T value, String& result)const
231{
232 const char* pResult = nullptr;
233 if (Base::TryGetEnumStringInternal(value, pResult))
234 {
235 // it's a simple not combined value
236 result = pResult;
237 return true;
238 }
239 // else must generate a combined string
240 if (TryFormatFlags(value, result))
241 {
242 return true;
243 }
244 // else format the raw value
245 std::ostringstream oss;
246 oss << "0x" << std::uppercase << std::hex << static_cast<U>(value);
247 result = oss.str();
248 return false;
249}
250
251template<class T, char Delimiter>
252inline bool EnumFlagsDictionary<T, Delimiter>::TryFormatFlags(T value, String& result)const
253{
254 std::ostringstream oss;
255 T resultValue = Base::zeroValue;
256
257 for (const auto& item : this->fields)
258 {
259 if (item.first == Base::zeroValue)
260 {
261 continue; // ignore zero fields, otherwise it would always match
262 }
263 if (IsFlag(item.first)) // check if the current item is a flag but not a regular enum value
264 {
265 if ((value & item.first) == item.first) // does value match current flag?
266 {
267 if (oss.tellp() == std::streampos(0)) // first token?
268 {
269 oss << item.second;
270 }
271 else
272 {
273 oss << Delimiter << item.second;
274 }
275 resultValue |= item.first;
276 }
277 }
278 else // the current item is not a flag but a regular enum value in a mixed Enum
279 {
280 if ((value & ~flagsMask) == item.first) // does value match current item?
281 {
282 if (oss.tellp() == std::streampos(0)) // first token?
283 {
284 oss << item.second;
285 }
286 else
287 {
288 oss << Delimiter << item.second;
289 }
290 resultValue |= item.first;
291 }
292 }
293 }
294 if (resultValue != value)
295 {
296 // the resulting string is incomplete, not all flags were included
297 return false;
298 }
299 // else
300 result = oss.str();
301 return true;
302}
303
304template<class T, char Delimiter>
305inline bool EnumFlagsDictionary<T, Delimiter>::TryParseCombinedEnumValue(const String& input, T& value)const
306{
307 static const char delimiters[] = { '|', ',', ';', Delimiter };
308 static const size_t count = sizeof(delimiters) / sizeof(char);
309
310 T result = Base::zeroValue;
311 String::Tokens tokens = input.Split(delimiters, count);
312 for (const String& token : tokens)
313 {
314 T flag = Base::zeroValue;
315 if (!this->TryGetEnumValueInternal(token, flag))
316 {
317 return false;
318 }
319 // else combine value with flag
320 result |= flag;
321 }
322 value = result;
323 return true;
324}
325
326} // end of namespace Arp
327
328#endif // ndef ARP_USE_ARP_SYSTEM_CORE
std::initializer_list< typename Fields::value_type > InitializerList
The initializer list type of the Fields container.
Definition: EnumDictionaryBase.hxx:24
typename std::underlying_type< T >::type U
The underlying integral type of the enum.
Definition: EnumDictionaryBase.hxx:25
std::ostream & WriteEnumString(std::ostream &os, T value) const
Writes the string of the specified enum value to the given stream.
Definition: EnumFlagsDictionary.ipp:69
T GetEnumValue(const String &fieldName) const
Returns the value of the specified enum string.
Definition: EnumFlagsDictionary.ipp:137
bool TryGetEnumValue(const String &fieldName, T &value) const
Tries to get the enum value of the specified string.
Definition: EnumFlagsDictionary.ipp:152
String GetEnumString(T value) const
Returns the string of the specified enum entry.
Definition: EnumFlagsDictionary.ipp:111
EnumFlagsDictionary(const InitializerList &fields)
Constructor passing the enum fields as initializer list.
Definition: EnumFlagsDictionary.ipp:19
bool TryGetEnumString(T value, String &result) const
Returns the string of the specified enum value or nullptr if the value could not be found.
Definition: EnumFlagsDictionary.ipp:127
std::istream & ReadEnumValue(std::istream &is, T &value) const
Tries to return the enum entry of the specified string.
Definition: EnumFlagsDictionary.ipp:95
Root namespace for the PLCnext API