d99c0e4fd377baf9805aeed9c4e1ffcc013e7c7f
[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/types.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         explicit Variant()              : _type(NOTHING) { _long   = 0;     }
53         explicit Variant(bool    value) : _type(BOOL)    { _bool   = value; }
54         explicit Variant(double  value) : _type(DOUBLE)  { _double = value; }
55         explicit Variant(float   value) : _type(FLOAT)   { _float  = value; }
56         explicit Variant(int32_t value) : _type(INT)     { _int    = value; }
57         explicit Variant(int64_t value) : _type(LONG)    { _long   = value; }
58
59         explicit Variant(const Evoral::MusicalTime& beats)
60                 : _type(BEATS)
61                 , _beats(beats)
62         {}
63
64         /** Make a variant of a specific string type (string types only) */
65         Variant(Type type, const std::string& value)
66                 : _type(type)
67                 , _string(value)
68         {}
69
70         /** Make a numeric variant from a double (numeric types only).
71          *
72          * If conversion is impossible, the variant will have type NOTHING.
73          */
74         Variant(Type type, double value)
75                 : _type(type)
76         {
77                 switch (type) {
78                 case BOOL:
79                         _bool = value != 0.0;
80                         break;
81                 case DOUBLE:
82                         _double = (double)value;
83                         break;
84                 case FLOAT:
85                         _float = (float)value;
86                         break;
87                 case INT:
88                         _int = (int32_t)lrint(std::max((double)INT32_MIN,
89                                                        std::min(value, (double)INT32_MAX)));
90                         break;
91                 case LONG:
92                         _long = (int64_t)lrint(std::max((double)INT64_MIN,
93                                                         std::min(value, (double)INT64_MAX)));
94                         break;
95                 default:
96                         _type = NOTHING;
97                         _long = 0;
98                 }
99         }
100
101         /** Convert a numeric variant to a double. */
102         double to_double() const {
103                 switch (_type) {
104                 case BOOL:   return _bool;
105                 case DOUBLE: return _double;
106                 case FLOAT:  return _float;
107                 case INT:    return _int;
108                 case LONG:   return _long;
109                 default:     return 0.0;
110                 }
111         }
112
113         bool   get_bool()   const { ensure_type(BOOL);   return _bool;   }
114         double get_double() const { ensure_type(DOUBLE); return _double; }
115         float  get_float()  const { ensure_type(FLOAT);  return _float;  }
116         int    get_int()    const { ensure_type(INT);    return _int;    }
117         long   get_long()   const { ensure_type(LONG);   return _long;   }
118
119         bool   operator==(bool v)   const { return _type == BOOL   && _bool == v; }
120         double operator==(double v) const { return _type == DOUBLE && _double == v; }
121         float  operator==(float v)  const { return _type == FLOAT  && _float == v; }
122         int    operator==(int v)    const { return _type == INT    && _int == v; }
123         long   operator==(long v)   const { return _type == LONG   && _long == v; }
124
125         Variant& operator=(bool v)   { _type = BOOL;   _bool = v;   return *this; }
126         Variant& operator=(double v) { _type = DOUBLE; _double = v; return *this; }
127         Variant& operator=(float v)  { _type = FLOAT;  _float = v;  return *this; }
128         Variant& operator=(int v)    { _type = INT;    _int = v;    return *this; }
129         Variant& operator=(long v)   { _type = LONG;   _long = v;   return *this; }
130
131         const std::string& get_path()   const { ensure_type(PATH);   return _string; }
132         const std::string& get_string() const { ensure_type(STRING); return _string; }
133         const std::string& get_uri()    const { ensure_type(URI);    return _string; }
134
135         bool operator==(const Variant& v) const {
136                 if (_type != v._type) {
137                         return false;
138                 }
139
140                 switch (_type) {
141                 case NOTHING: return true;
142                 case BEATS:   return _beats  == v._beats;
143                 case BOOL:    return _bool   == v._bool;
144                 case DOUBLE:  return _double == v._double;
145                 case FLOAT:   return _float  == v._float;
146                 case INT:     return _int    == v._int;
147                 case LONG:    return _long   == v._long;
148                 case PATH:
149                 case STRING:
150                 case URI:     return _string == v._string;
151                 }
152
153                 return false;
154         }
155
156         bool operator==(const Evoral::MusicalTime& v) const {
157                 return _type == BEATS && _beats == v;
158         }
159
160         Variant& operator=(Evoral::MusicalTime v) {
161                 _type  = BEATS;
162                 _beats = v;
163                 return *this;
164         }
165
166         const Evoral::MusicalTime& get_beats() const {
167                 ensure_type(BEATS); return _beats;
168         }
169
170         Type type() const { return _type; }
171
172         static bool type_is_numeric(Type type) {
173                 switch (type) {
174                 case BOOL: case DOUBLE: case FLOAT: case INT: case LONG:
175                         return true;
176                 default:
177                         return false;
178                 }
179         }
180
181 private:
182         static const char* type_name(const Type type) {
183                 static const char* names[] = {
184                         "bool", "double", "float", "int", "long", "path", "string", "uri"
185                 };
186
187                 return names[type];
188         }
189
190         void ensure_type(const Type type) const {
191                 if (_type != type) {
192                         throw std::domain_error(
193                                 string_compose("get_%1 called on %2 variant",
194                                                type_name(type), type_name(_type)));
195                 }
196         }
197
198         Type                _type;    ///< Type tag
199         std::string         _string;  ///< PATH, STRING, URI
200         Evoral::MusicalTime _beats;   ///< BEATS
201
202         // Union of all primitive numeric types
203         union {
204                 bool    _bool;
205                 double  _double;
206                 float   _float;
207                 int32_t _int;
208                 int64_t _long;
209         };
210 };
211
212 } // namespace ARDOUR
213
214 #endif // __ardour_variant_h__