Caffa  1.1.0
C++ Application Framework for Embedded Systems with introspection
cafAppEnum.h
1 // ##################################################################################################
2 //
3 // Caffa
4 // Copyright (C) 2011-2013 Ceetron AS
5 // Copyright (C) 2013- Ceetron Solutions AS
6 // Copyright (C) 2022- Kontur AS
7 //
8 // GNU Lesser General Public License Usage
9 // This library is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation; either version 2.1 of the License, or
12 // (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful, but WITHOUT ANY
15 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 // FITNESS FOR A PARTICULAR PURPOSE.
17 //
18 // See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
19 // for more details.
20 //
21 // ##################################################################################################
22 
23 #pragma once
24 
25 #include "cafAssert.h"
26 #include "cafLogger.h"
27 #include "cafPortableDataType.h"
28 
29 #include <concepts>
30 #include <optional>
31 #include <stdexcept>
32 #include <string>
33 #include <type_traits>
34 #include <vector>
35 
36 namespace caffa
37 {
38 template <typename T>
39 concept enum_type = std::is_enum<T>::value;
40 
63 template <typename Enum>
64  requires enum_type<Enum>
65 class AppEnum
66 {
67 public:
68  using DataType = Enum;
69 
70  AppEnum()
71  {
72  setUp();
73  m_value = m_defaultValue;
74  }
75  AppEnum( Enum value )
76  : m_value( value )
77  {
78  setUp();
79  }
80  AppEnum( const std::string& value )
81  {
82  setUp();
83  m_value = m_defaultValue;
84  setFromLabel( value );
85  }
86 
87  Enum value() const
88  {
89  if ( m_value )
90  {
91  return *m_value;
92  }
93  throw std::runtime_error( "The AppEnum has no value!" );
94  }
95 
96  auto operator<=>( const AppEnum& rhs ) const = default;
97 
98  AppEnum& operator=( Enum value )
99  {
100  m_value = value;
101  return *this;
102  }
103 
104  void setFromLabel( const std::string& label )
105  {
106  auto value = enumVal( label );
107  if ( value )
108  {
109  m_value = value;
110  }
111  else
112  {
113  throw std::runtime_error( label + " is not a valid option" );
114  }
115  }
116 
117  void setFromIndex( size_t index )
118  {
119  auto value = enumVal( index );
120  if ( value )
121  {
122  m_value = value;
123  }
124  else
125  {
126  throw std::runtime_error( std::to_string( index ) + " is not a valid option index" );
127  }
128  }
129 
130  std::optional<Enum> enumVal( const std::string& label ) const
131  {
132  for ( auto [entryValue, entryLabel] : m_mapping )
133  {
134  if ( entryLabel == label )
135  {
136  return entryValue;
137  }
138  }
139 
140  CAFFA_ERROR( "No label " << label << " in AppEnum" );
141  for ( const auto& [entry, entryLabel] : m_mapping )
142  {
143  CAFFA_ERROR( "Found label " << entryLabel << "(" << static_cast<int>( entry ) << ")" );
144  }
145  return std::nullopt;
146  }
147 
148  std::optional<Enum> enumVal( size_t index ) const
149  {
150  if ( index < m_mapping.size() )
151  {
152  return m_mapping[index].first;
153  }
154  return std::nullopt;
155  }
156 
157  size_t size() const { return m_mapping.size(); }
158 
159  std::vector<std::string> labels() const
160  {
161  std::vector<std::string> labelList;
162  for ( const auto& [ignore, label] : m_mapping )
163  {
164  labelList.push_back( label );
165  }
166  return labelList;
167  }
168 
169  std::string label() const { return this->label( value() ); }
170 
171  std::string label( Enum enumValue ) const
172  {
173  std::string label;
174  for ( const auto& [entry, entryLabel] : m_mapping )
175  {
176  if ( enumValue == entry )
177  {
178  label = entryLabel;
179  break;
180  }
181  }
182 
183  if ( label.empty() )
184  {
185  CAFFA_ERROR( "No value " << static_cast<int>( enumValue ) << " in AppEnum" );
186  for ( const auto& [entry, entryLabel] : m_mapping )
187  {
188  CAFFA_ERROR( "Found label " << entryLabel << "(" << static_cast<int>( entry ) << ")" );
189  }
190  throw std::runtime_error( "AppEnum does not have the value " + std::to_string( static_cast<int>( enumValue ) ) );
191  }
192 
193  return label;
194  }
195 
196  size_t index( Enum enumValue ) const
197  {
198  std::optional<size_t> foundIndex;
199  for ( size_t i = 0; i < m_mapping.size(); ++i )
200  {
201  if ( m_mapping[i].first == enumValue )
202  {
203  foundIndex = i;
204  break;
205  }
206  }
207 
208  if ( !foundIndex.has_value() )
209  {
210  CAFFA_ERROR( "No value " << static_cast<int>( value ) << " in AppEnum" );
211  for ( const auto& [entry, label] : m_mapping )
212  {
213  CAFFA_ERROR( "Found label " << label << "(" << static_cast<int>( entry ) << ")" );
214  }
215 
216  throw std::runtime_error( "AppEnum does not have the value " + std::to_string( static_cast<int>( enumValue ) ) );
217  }
218  return *foundIndex;
219  }
220 
221  // Static interface to access the properties of the enum definition
222 
223  static bool isValid( const std::string& label ) { return AppEnum<Enum>().enumVal( label ).has_value(); }
224  static bool isValid( size_t index ) { return AppEnum<Enum>().enumval( index ).has_value(); }
225  static size_t validSize() { return AppEnum<Enum>().size(); }
226 
227  static std::vector<std::string> validLabels() { return AppEnum<Enum>().labels(); }
228 
229  static size_t getIndex( Enum enumValue ) { return AppEnum<Enum>().index( enumValue ); }
230  static std::string getLabel( Enum enumValue ) { return AppEnum<Enum>().label( enumValue ); }
231 
232 private:
233  //==================================================================================================
237  //==================================================================================================
238  void setUp();
239  void addItem( Enum enumVal, const std::string& label ) { m_mapping.push_back( std::make_pair( enumVal, label ) ); }
240 
241  void setDefault( Enum defaultEnumValue ) { m_defaultValue = defaultEnumValue; }
242 
243  std::optional<Enum> m_value;
244  std::optional<Enum> m_defaultValue;
245 
246  std::vector<std::pair<Enum, std::string>> m_mapping;
247 };
248 
249 template <typename EnumType>
250 struct PortableDataType<AppEnum<EnumType>>
251 {
252  static std::string name()
253  {
254  auto labels = AppEnum<EnumType>::validLabels();
255  std::stringstream ss;
256  ss << "AppEnum(";
257  for ( size_t i = 0; i < labels.size(); ++i )
258  {
259  if ( i > 0u ) ss << ",";
260  ss << labels[i];
261  }
262  ss << ")";
263  return ss.str();
264  }
265 };
266 //==================================================================================================
269 //==================================================================================================
270 
271 template <typename Enum>
272 std::istream& operator>>( std::istream& str, caffa::AppEnum<Enum>& appEnum )
273 {
274  std::string label;
275  str >> label;
276 
277  appEnum.setFromLabel( label );
278 
279  return str;
280 }
281 
282 template <typename Enum>
283 std::ostream& operator<<( std::ostream& str, const caffa::AppEnum<Enum>& appEnum )
284 {
285  auto value = appEnum.value();
286  str << appEnum.label( value );
287  return str;
288 }
289 
290 } // namespace caffa
std::istream & operator>>(std::istream &str, caffa::AppEnum< Enum > &appEnum)
Definition: cafAppEnum.h:272
Definition: cafPortableDataType.h:35
Definition: cafAppEnum.h:65
Main Caffa namespace.
Definition: cafApplication.h:30