const bool METHOD() const makes no sense
[ardour.git] / libs / ardour / ardour / variant.h
1 /*
2     Copyright (C) 2014 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify it
6     under the terms of the GNU General Public License as published by the Free
7     Software Foundation; either version 2 of the License, or (at your option)
8     any later version.
9
10     This program is distributed in the hope that it will be useful, but WITHOUT
11     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13     for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifndef __ardour_variant_h__
21 #define __ardour_variant_h__
22
23 #include <stdint.h>
24 #include <limits.h>
25
26 #include <algorithm>
27 #include <stdexcept>
28
29 #include "ardour/libardour_visibility.h"
30 #include "evoral/Beats.hpp"
31 #include "pbd/compose.h"
32
33 namespace ARDOUR {
34
35 /** A value with dynamic type (tagged union). */
36 class LIBARDOUR_API Variant
37 {
38 public:
39         enum Type {
40                 NOTHING, ///< Nothing (void)
41                 BEATS,   ///< Beats+ticks
42                 BOOL,    ///< Boolean
43                 DOUBLE,  ///< C double (64-bit IEEE-754)
44                 FLOAT,   ///< C float (32-bit IEEE-754)
45                 INT,     ///< Signed 32-bit int
46                 LONG,    ///< Signed 64-bit int
47                 PATH,    ///< File path string
48                 STRING,  ///< Raw string (no semantics)
49                 URI      ///< URI string
50         };
51
52         Variant() : _type(NOTHING) { _long = 0; }
53
54         explicit Variant(bool    value) : _type(BOOL)    { _bool   = value; }
55         explicit Variant(double  value) : _type(DOUBLE)  { _double = value; }
56         explicit Variant(float   value) : _type(FLOAT)   { _float  = value; }
57         explicit Variant(int32_t value) : _type(INT)     { _int    = value; }
58         explicit Variant(int64_t value) : _type(LONG)    { _long   = value; }
59
60         explicit Variant(const Evoral::Beats& beats)
61                 : _type(BEATS)
62                 , _beats(beats)
63         {}
64
65         /** Make a variant of a specific string type (string types only) */
66         Variant(Type type, const std::string& value)
67                 : _type(type)
68                 , _string(value)
69         {}
70
71         /** Make a numeric variant from a double (numeric types only).
72          *
73          * If conversion is impossible, the variant will have type NOTHING.
74          */
75         Variant(Type type, double value)
76                 : _type(type)
77         {
78                 switch (type) {
79                 case BOOL:
80                         _bool = value != 0.0;
81                         break;
82                 case DOUBLE:
83                         _double = (double)value;
84                         break;
85                 case FLOAT:
86                         _float = (float)value;
87                         break;
88                 case INT:
89                         _int = (int32_t)lrint(std::max((double)INT32_MIN,
90                                                        std::min(value, (double)INT32_MAX)));
91                         break;
92                 case LONG:
93                         _long = (int64_t)lrint(std::max((double)INT64_MIN,
94                                                         std::min(value, (double)INT64_MAX)));
95                         break;
96                 case BEATS:
97                         _beats = Evoral::Beats(value);
98                         break;
99                 default:
100                         _type = NOTHING;
101                         _long = 0;
102                 }
103         }
104
105         /** Convert a numeric variant to a double. */
106         double to_double() const {
107                 switch (_type) {
108                 case BOOL:   return _bool;
109                 case DOUBLE: return _double;
110                 case FLOAT:  return _float;
111                 case INT:    return _int;
112                 case LONG:   return _long;
113                 case BEATS:  return _beats.to_double();
114                 default:     return 0.0;
115                 }
116         }
117
118         bool   get_bool()   const { ensure_type(BOOL);   return _bool;   }
119         double get_double() const { ensure_type(DOUBLE); return _double; }
120         float  get_float()  const { ensure_type(FLOAT);  return _float;  }
121         int    get_int()    const { ensure_type(INT);    return _int;    }
122         long   get_long()   const { ensure_type(LONG);   return _long;   }
123
124         bool   operator==(bool v)   const { return _type == BOOL   && _bool == v; }
125         double operator==(double v) const { return _type == DOUBLE && _double == v; }
126         float  operator==(float v)  const { return _type == FLOAT  && _float == v; }
127         int    operator==(int v)    const { return _type == INT    && _int == v; }
128         long   operator==(long v)   const { return _type == LONG   && _long == v; }
129
130         Variant& operator=(bool v)   { _type = BOOL;   _bool = v;   return *this; }
131         Variant& operator=(double v) { _type = DOUBLE; _double = v; return *this; }
132         Variant& operator=(float v)  { _type = FLOAT;  _float = v;  return *this; }
133         Variant& operator=(int v)    { _type = INT;    _int = v;    return *this; }
134         Variant& operator=(long v)   { _type = LONG;   _long = v;   return *this; }
135
136         const std::string& get_path()   const { ensure_type(PATH);   return _string; }
137         const std::string& get_string() const { ensure_type(STRING); return _string; }
138         const std::string& get_uri()    const { ensure_type(URI);    return _string; }
139
140         bool operator==(const Variant& v) const {
141                 if (_type != v._type) {
142                         return false;
143                 }
144
145                 switch (_type) {
146                 case NOTHING: return true;
147                 case BEATS:   return _beats  == v._beats;
148                 case BOOL:    return _bool   == v._bool;
149                 case DOUBLE:  return _double == v._double;
150                 case FLOAT:   return _float  == v._float;
151                 case INT:     return _int    == v._int;
152                 case LONG:    return _long   == v._long;
153                 case PATH:
154                 case STRING:
155                 case URI:     return _string == v._string;
156                 }
157
158                 return false;
159         }
160
161         bool operator==(const Evoral::Beats& v) const {
162                 return _type == BEATS && _beats == v;
163         }
164
165         bool operator!() const { return _type == NOTHING; }
166
167         Variant& operator=(Evoral::Beats v) {
168                 _type  = BEATS;
169                 _beats = v;
170                 return *this;
171         }
172
173         const Evoral::Beats& get_beats() const {
174                 ensure_type(BEATS); return _beats;
175         }
176
177         Type type() const { return _type; }
178
179         static bool type_is_numeric(Type type) {
180                 switch (type) {
181                 case BOOL: case DOUBLE: case FLOAT: case INT: case LONG: case BEATS:
182                         return true;
183                 default:
184                         return false;
185                 }
186         }
187
188 private:
189         static const char* type_name(const Type type) {
190                 static const char* names[] = {
191                         "bool", "double", "float", "int", "long", "path", "string", "uri"
192                 };
193
194                 return names[type];
195         }
196
197         void ensure_type(const Type type) const {
198                 if (_type != type) {
199                         throw std::domain_error(
200                                 string_compose("get_%1 called on %2 variant",
201                                                type_name(type), type_name(_type)));
202                 }
203         }
204
205         Type          _type;    ///< Type tag
206         std::string   _string;  ///< PATH, STRING, URI
207         Evoral::Beats _beats;   ///< BEATS
208
209         // Union of all primitive numeric types
210         union {
211                 bool    _bool;
212                 double  _double;
213                 float   _float;
214                 int32_t _int;
215                 int64_t _long;
216         };
217 };
218
219 } // namespace ARDOUR
220
221 #endif // __ardour_variant_h__