1 /* This file is part of the Pangolin Project.
2  * http://github.com/stevenlovegrove/Pangolin
3  *
4  * Copyright (c) 2014 Steven Lovegrove
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #pragma once
29 
30 #include <stdexcept>
31 #include <string.h>
32 #include <cmath>
33 
34 #include <pangolin/var/varvalue.h>
35 #include <pangolin/var/varwrapper.h>
36 #include <pangolin/var/varstate.h>
37 
38 namespace pangolin
39 {
40 
41 template<typename T>
InitialiseNewVarMetaGeneric(VarValue<T> & v,const std::string & name)42 inline void InitialiseNewVarMetaGeneric(
43     VarValue<T>& v, const std::string& name
44 ) {
45     // Initialise meta parameters
46     const std::vector<std::string> parts = pangolin::Split(name,'.');
47     v.Meta().full_name = name;
48     v.Meta().friendly = parts.size() > 0 ? parts[parts.size()-1] : "";
49     v.Meta().range[0] = 0.0;
50     v.Meta().range[1] = 0.0;
51     v.Meta().increment = 0.0;
52     v.Meta().flags = META_FLAG_NONE;
53     v.Meta().logscale = false;
54     v.Meta().generic = true;
55 
56     VarState::I().NotifyNewVar<T>(name, v);
57 }
58 
59 template<typename T>
60 inline void InitialiseNewVarMeta(
61     VarValue<T>& v, const std::string& name,
62     double min = 0, double max = 0, int flags = META_FLAG_TOGGLE,
63     bool logscale = false
64 ) {
65     // Initialise meta parameters
66     const std::vector<std::string> parts = pangolin::Split(name,'.');
67     v.Meta().full_name = name;
68     v.Meta().friendly = parts.size() > 0 ? parts[parts.size()-1] : "";
69     v.Meta().range[0] = min;
70     v.Meta().range[1] = max;
71     if (std::is_integral<T>::value) {
72         v.Meta().increment = 1.0;
73     } else {
74         v.Meta().increment = (max - min) / 100.0;
75     }
76     v.Meta().flags = flags;
77     v.Meta().logscale = logscale;
78     v.Meta().generic = false;
79 
80     VarState::I().NotifyNewVar<T>(name, v);
81 }
82 
83 template<typename T>
84 class Var
85 {
86 public:
87     static T& Attach(
88         const std::string& name, T& variable,
89         double min, double max, bool logscale = false
90     ) {
91         // Find name in VarStore
92         VarValueGeneric*& v = VarState::I()[name];
93         if(v) {
94             throw std::runtime_error(std::string("Var with the following name already exists: ") + name);
95         }else{
96             // new VarRef<T> (owned by VarStore)
97             VarValue<T&>* nv = new VarValue<T&>(variable);
98             v = nv;
99             if(logscale) {
100                 if (min <= 0 || max <= 0) {
101                     throw std::runtime_error("LogScale: range of numbers must be positive!");
102                 }
103                 InitialiseNewVarMeta<T&>(*nv, name, std::log(min), std::log(max), META_FLAG_TOGGLE, logscale);
104             }else{
105                 InitialiseNewVarMeta<T&>(*nv, name, min, max, META_FLAG_TOGGLE, logscale);
106             }
107         }
108         return variable;
109     }
110 
111     static T& Attach(
112         const std::string& name, T& variable, int flags = META_FLAG_NONE
113         ) {
114         // Find name in VarStore
115         VarValueGeneric*& v = VarState::I()[name];
116         if (v) {
117             throw std::runtime_error(std::string("Var with the following name already exists: ") + name);
118         }
119         else{
120             // new VarRef<T> (owned by VarStore)
121             VarValue<T&>* nv = new VarValue<T&>(variable);
122             v = nv;
123             InitialiseNewVarMeta<T&>(*nv, name, 0.0, 0.0, flags);
124         }
125         return variable;
126     }
127 
Attach(const std::string & name,T & variable,bool toggle)128     static T& Attach(
129         const std::string& name, T& variable, bool toggle
130         ) {
131         return Attach(name, variable, toggle ? META_FLAG_TOGGLE : META_FLAG_NONE);
132     }
133 
~Var()134     ~Var()
135     {
136         delete ptr;
137     }
138 
Var(VarValueGeneric & v)139     Var( VarValueGeneric& v )
140         : ptr(0)
141     {
142         InitialiseFromGeneric(&v);
143     }
144 
145 
Var(const std::string & name)146     Var( const std::string& name )
147         : ptr(0)
148     {
149         // Find name in VarStore
150         VarValueGeneric*& v = VarState::I()[name];
151         if(v && !v->Meta().generic) {
152             InitialiseFromGeneric(v);
153         }else{
154             // new VarValue<T> (owned by VarStore)
155             VarValue<T>* nv;
156             if(v) {
157                 // Specialise generic variable
158                 nv = new VarValue<T>( Convert<T,std::string>::Do( v->str->Get() ) );
159                 delete v;
160             }else{
161                 nv = new VarValue<T>( T() );
162             }
163             v = nv;
164             var = nv;
165             InitialiseNewVarMeta(*nv, name);
166         }
167     }
168 
169     Var(const std::string& name, const T& value, int flags = META_FLAG_NONE)
170         : ptr(0)
171     {
172         // Find name in VarStore
173         VarValueGeneric*& v = VarState::I()[name];
174         if(v && !v->Meta().generic) {
175             InitialiseFromGeneric(v);
176         }else{
177             // new VarValue<T> (owned by VarStore)
178             VarValue<T>* nv;
179             if(v) {
180                 // Specialise generic variable
181                 nv = new VarValue<T>( Convert<T,std::string>::Do( v->str->Get() ) );
182                 delete v;
183             }else{
184                 nv = new VarValue<T>(value);
185             }
186             v = nv;
187             var = nv;
188             InitialiseNewVarMeta(*nv, name, 0, 1, flags);
189         }
190     }
191 
Var(const std::string & name,const T & value,bool toggle)192     Var(const std::string& name, const T& value, bool toggle)
193         : Var(name, value, toggle ? META_FLAG_TOGGLE : META_FLAG_NONE)
194     {
195     }
196 
197     Var(
198         const std::string& name, const T& value,
199         double min, double max, bool logscale = false
200     ) : ptr(0)
201     {
202         // Find name in VarStore
203         VarValueGeneric*& v = VarState::I()[name];
204         if(v && !v->Meta().generic) {
205             InitialiseFromGeneric(v);
206         }else{
207             // new VarValue<T> (owned by VarStore)
208             VarValue<T>* nv;
209             if(v) {
210                 // Specialise generic variable
211                 nv = new VarValue<T>( Convert<T,std::string>::Do( v->str->Get() ) );
212                 delete v;
213             }else{
214                 nv = new VarValue<T>(value);
215             }
216             var = nv;
217             v = nv;
218             if(logscale) {
219                 if (min <= 0 || max <= 0) {
220                     throw std::runtime_error("LogScale: range of numbers must be positive!");
221                 }
222                 InitialiseNewVarMeta(*nv, name, std::log(min), std::log(max), META_FLAG_TOGGLE, true);
223             }else{
224                 InitialiseNewVarMeta(*nv, name, min, max);
225             }
226         }
227     }
228 
Reset()229     void Reset()
230     {
231         var->Reset();
232     }
233 
Get()234     const T& Get() const
235     {
236         try{
237             return var->Get();
238         }catch(const BadInputException&)
239         {
240             const_cast<Var<T> *>(this)->Reset();
241             return var->Get();
242         }
243     }
244 
245     operator const T& () const
246     {
247         return Get();
248     }
249 
250     const T* operator->()
251     {
252         try{
253             return &(var->Get());
catch(const BadInputException &)254         }catch(const BadInputException&)
255         {
256             Reset();
257             return &(var->Get());
258         }
259     }
260 
261     void operator=(const T& val)
262     {
263         var->Set(val);
264     }
265 
266     void operator=(const Var<T>& v)
267     {
268         var->Set(v.var->Get());
269     }
270 
Meta()271     VarMeta& Meta()
272     {
273         return var->Meta();
274     }
275 
GuiChanged()276     bool GuiChanged()
277     {
278         if(var->Meta().gui_changed) {
279             var->Meta().gui_changed = false;
280             return true;
281         }
282         return false;
283     }
284 
Ref()285     VarValueT<T>& Ref()
286     {
287         return *var;
288     }
289 
290 protected:
291     // Initialise from existing variable, obtain data / accessor
InitialiseFromGeneric(VarValueGeneric * v)292     void InitialiseFromGeneric(VarValueGeneric* v)
293     {
294         if( !strcmp(v->TypeId(), typeid(T).name()) ) {
295             // Same type
296             var = (VarValueT<T>*)(v);
297         }else if( std::is_same<T,std::string>::value ) {
298             // Use types string accessor
299             var = (VarValueT<T>*)(v->str);
300         }else if( !strcmp(v->TypeId(), typeid(bool).name() ) ) {
301             // Wrapper, owned by this object
302             ptr = new VarWrapper<T,bool>( *(VarValueT<bool>*)v );
303             var = ptr;
304         }else if( !strcmp(v->TypeId(), typeid(short).name() ) ) {
305             // Wrapper, owned by this object
306             ptr = new VarWrapper<T,short>( *(VarValueT<short>*)v );
307             var = ptr;
308         }else if( !strcmp(v->TypeId(), typeid(int).name() ) ) {
309             // Wrapper, owned by this object
310             ptr = new VarWrapper<T,int>( *(VarValueT<int>*)v );
311             var = ptr;
312         }else if( !strcmp(v->TypeId(), typeid(long).name() ) ) {
313             // Wrapper, owned by this object
314             ptr = new VarWrapper<T,long>( *(VarValueT<long>*)v );
315             var = ptr;
316         }else if( !strcmp(v->TypeId(), typeid(float).name() ) ) {
317             // Wrapper, owned by this object
318             ptr = new VarWrapper<T,float>( *(VarValueT<float>*)v );
319             var = ptr;
320         }else if( !strcmp(v->TypeId(), typeid(double).name() ) ) {
321             // Wrapper, owned by this object
322             ptr = new VarWrapper<T,double>( *(VarValueT<double>*)v );
323             var = ptr;
324         }else{
325             // other types: have to go via string
326             // Wrapper, owned by this object
327             ptr = new VarWrapper<T,std::string>( *(v->str) );
328             var = ptr;
329         }
330     }
331 
332     // Holds reference to stored variable object
333     // N.B. mutable because it is a cached value and Get() is advertised as const.
334     mutable VarValueT<T>* var;
335 
336     // ptr is non-zero if this object owns the object variable (a wrapper)
337     VarValueT<T>* ptr;
338 };
339 
340 }
341