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