Caffa  1.1.0
C++ Application Framework for Embedded Systems with introspection
cafAppEnum.h
1 // ##################################################################################################
2 //
3 // Custom Visualization Core library
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 "cafJsonDataType.h"
27 #include "cafPortableDataType.h"
28 
29 #include <nlohmann/json.hpp>
30 
31 #include <concepts>
32 #include <iostream>
33 #include <list>
34 #include <stdexcept>
35 #include <string>
36 #include <type_traits>
37 #include <vector>
38 
39 namespace caffa
40 {
41 template <typename T>
42 concept enum_type = std::is_enum<T>::value;
43 
66 template <typename Enum>
67  requires enum_type<Enum>
68 class AppEnum
69 {
70 public:
71  using DataType = Enum;
72 
73  AppEnum() { m_value = EnumMapper::instance()->defaultValue(); }
74  AppEnum( Enum value )
75  : m_value( value )
76  {
77  }
78  AppEnum( const std::string& value )
79  {
80  m_value = EnumMapper::instance()->defaultValue();
81  setFromLabel( value );
82  }
83 
84  auto operator<=>( const AppEnum& rhs ) const = default;
85 
86  Enum value() const { return m_value; }
87  size_t index() const { return EnumMapper::instance()->index( m_value ); }
88  std::string label() const { return EnumMapper::instance()->label( m_value ); }
89 
90  AppEnum& operator=( Enum value )
91  {
92  m_value = value;
93  return *this;
94  }
95 
96  void setFromLabel( const std::string& label )
97  {
98  if ( !EnumMapper::instance()->enumVal( m_value, label ) )
99  {
100  throw std::runtime_error( label + " is not a valid option" );
101  }
102  }
103 
104  void setFromIndex( size_t index )
105  {
106  if ( !EnumMapper::instance()->enumVal( m_value, index ) )
107  {
108  throw std::runtime_error( std::to_string( index ) + " is not a valid option index" );
109  }
110  }
111 
112  // Static interface to access the properties of the enum definition
113 
114  static bool isValid( const std::string& label ) { return EnumMapper::instance()->isValid( label ); }
115  static bool isValid( size_t index ) { return index < EnumMapper::instance()->size(); }
116  static size_t size() { return EnumMapper::instance()->size(); }
117 
118  static std::vector<std::string> labels() { return EnumMapper::instance()->labels(); }
119  static auto fromIndex( size_t idx )
120  {
121  Enum val;
122  if ( !EnumMapper::instance()->enumVal( val, idx ) )
123  {
124  throw std::runtime_error( std::to_string( idx ) + " is not a valid option index" );
125  }
126  return AppEnum<Enum>( val );
127  }
128  static auto fromLabel( const std::string& label ) { return AppEnum<Enum>( label ); }
129  static size_t index( Enum enumValue ) { return EnumMapper::instance()->index( enumValue ); }
130  static std::string label( Enum enumValue ) { return EnumMapper::instance()->label( enumValue ); }
131  static std::string labelFromIndex( size_t idx ) { return label( fromIndex( idx ).value() ); }
132 
133 private:
134  //==================================================================================================
138  //==================================================================================================
139  static void setUp();
140  static void addItem( Enum enumVal, const std::string& label ) { EnumMapper::instance()->addItem( enumVal, label ); }
141 
142  static void setDefault( Enum defaultEnumValue ) { EnumMapper::instance()->setDefault( defaultEnumValue ); }
143 
144  Enum m_value;
145 
146  //==================================================================================================
151  //==================================================================================================
152 
153  class EnumMapper
154  {
155  public:
156  void addItem( Enum enumVal, const std::string& label )
157  {
158  instance()->m_mapping.push_back( std::make_pair( enumVal, label ) );
159  }
160 
161  static EnumMapper* instance()
162  {
163  static EnumMapper storedInstance;
164  static bool isInitialized = false;
165  if ( !isInitialized )
166  {
167  isInitialized = true;
169  }
170  return &storedInstance;
171  }
172 
173  void setDefault( Enum defaultEnumValue )
174  {
175  m_defaultValue = defaultEnumValue;
176  m_defaultValueIsSet = true;
177  }
178 
179  Enum defaultValue() const
180  {
181  if ( m_defaultValueIsSet )
182  {
183  return m_defaultValue;
184  }
185  else
186  {
187  CAFFA_ASSERT( m_mapping.size() );
188  return m_mapping[0].first;
189  }
190  }
191 
192  bool isValid( const std::string& label ) const
193  {
194  size_t idx;
195  for ( idx = 0; idx < m_mapping.size(); ++idx )
196  {
197  if ( label == m_mapping[idx].second ) return true;
198  }
199 
200  return false;
201  }
202 
203  size_t size() const { return m_mapping.size(); }
204 
205  bool enumVal( Enum& value, const std::string& label ) const
206  {
207  value = defaultValue();
208  size_t idx;
209  for ( idx = 0; idx < m_mapping.size(); ++idx )
210  {
211  if ( label == m_mapping[idx].second )
212  {
213  value = m_mapping[idx].first;
214  return true;
215  }
216  }
217  return false;
218  }
219 
220  bool enumVal( Enum& value, size_t index ) const
221  {
222  value = defaultValue();
223  if ( index < m_mapping.size() )
224  {
225  value = m_mapping[index].first;
226  return true;
227  }
228  else
229  return false;
230  }
231 
232  size_t index( Enum value ) const
233  {
234  for ( size_t i = 0; i < m_mapping.size(); ++i )
235  {
236  if ( value == m_mapping[i].first ) return i;
237  }
238  throw std::runtime_error( "AppEnum does not have the value " + std::to_string( static_cast<int>( value ) ) );
239  }
240 
241  std::string label( Enum value ) const
242  {
243  for ( const auto& [enumVal, label] : m_mapping )
244  {
245  if ( value == enumVal ) return label;
246  }
247  throw std::runtime_error( "AppEnum does not have the value " + std::to_string( static_cast<int>( value ) ) );
248  }
249 
250  std::vector<std::string> labels() const
251  {
252  std::vector<std::string> labelList;
253  for ( const auto& [ignore, label] : m_mapping )
254  {
255  labelList.push_back( label );
256  }
257  return labelList;
258  }
259 
260  private:
261  EnumMapper()
262  : m_defaultValueIsSet( false )
263  {
264  }
265 
266  std::vector<std::pair<Enum, std::string>> m_mapping;
267  Enum m_defaultValue;
268  bool m_defaultValueIsSet;
269  };
270 };
271 
272 template <typename EnumType>
273 struct PortableDataType<AppEnum<EnumType>>
274 {
275  static std::string name()
276  {
277  auto labels = AppEnum<EnumType>::labels();
278  std::stringstream ss;
279  ss << "AppEnum(";
280  for ( size_t i = 0; i < labels.size(); ++i )
281  {
282  if ( i > 0u ) ss << ",";
283  ss << labels[i];
284  }
285  ss << ")";
286  return ss.str();
287  }
288 };
289 
290 template <typename EnumType>
291 struct PortableDataType<std::vector<AppEnum<EnumType>>>
292 {
293  static std::string name()
294  {
295  auto labels = AppEnum<EnumType>::labels();
296  std::stringstream ss;
297  ss << "AppEnum[](";
298  for ( size_t i = 0; i < labels.size(); ++i )
299  {
300  if ( i > 0u ) ss << ",";
301  ss << labels[i];
302  }
303  ss << ")";
304  return ss.str();
305  }
306 };
307 
308 template <typename EnumType>
309 struct JsonDataType<AppEnum<EnumType>>
310 {
311  static nlohmann::json type()
312  {
313  auto values = nlohmann::json::array();
314  for ( auto entry : AppEnum<EnumType>::labels() )
315  {
316  values.push_back( entry );
317  }
318  auto object = nlohmann::json::object();
319  object["enum"] = values;
320  return object;
321  }
322 };
323 //==================================================================================================
326 //==================================================================================================
327 
328 template <typename Enum>
329 std::istream& operator>>( std::istream& str, caffa::AppEnum<Enum>& appEnum )
330 {
331  std::string label;
332  str >> label;
333 
334  appEnum.setFromLabel( label );
335 
336  return str;
337 }
338 
339 template <typename Enum>
340 std::ostream& operator<<( std::ostream& str, const caffa::AppEnum<Enum>& appEnum )
341 {
342  std::string label = appEnum.label();
343  str << appEnum.label();
344  return str;
345 }
346 
347 template <typename Enum>
348 void to_json( nlohmann::json& jsonValue, const AppEnum<Enum>& appEnum )
349 {
350  std::stringstream stream;
351  stream << appEnum;
352  jsonValue = stream.str();
353 }
354 
355 template <typename Enum>
356 void from_json( const nlohmann::json& jsonValue, AppEnum<Enum>& appEnum )
357 {
358  std::stringstream stream( jsonValue.get<std::string>() );
359  stream >> appEnum;
360 }
361 
362 } // namespace caffa
Definition: cafAppEnum.h:69
Main Caffa namespace.
Definition: __init__.py:1
std::istream & operator>>(std::istream &str, caffa::AppEnum< Enum > &appEnum)
Definition: cafAppEnum.h:329
Definition: cafJsonDataType.h:37
Definition: cafPortableDataType.h:35