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