CompoundUnit.h
1/* Distributed under the Apache License, Version 2.0.
2 See accompanying NOTICE file for details.*/
3
4//----------------------------------------------------------------------------
11//----------------------------------------------------------------------------
12#pragma once
13
14// Let's try to make this as lightweight as possible so that CCompoundUnit
15// objects can be efficiently manipulated within the context of an enclosing
16// physical quantity object (name TBD: PhysicsQuantity? DimensionedQuantity?
17// How about just Quantity?) when arithmetic with them needs to be done. This
18// means maximizing use of inline functions, even those that manipulate the
19// CUEVecType elements, which in turn requires dealing with Microsoft's BS for
20// exporting STL classes and classes containing STL data objects as data members.
21// (See http://support.microsoft.com/kb/168958) The only STL class that you can do
22// this with is vector<>, but fortunately that's all we need here. Usually, I get
23// around this by declaring the data objects as pointers to STL template
24// instantiations that I dynamically allocate and manipulate in non-inline functions,
25// but since we're going for inline methods here, doing so wouldn't buy me anything,
26// so I may as well make them first class data members. That may have the added
27// benefit of coaxing out any helpful warning messages that the compiler might
28// mistakenly think are not relevant when the data members are merely pointers to
29// STL classes. -CRV
30
31// Note: The ordering of the following two declarations is vital. The above-mentioned
32// knowledge base article only talks about exporting the vector class that I'm
33// explicitly instantiating, but that results in an error message about the allocator
34// needing to be exported. Adding the allocator export after the vector export didn't
35// solve the problem. But the paragraph about nested classes in the KB article alludes to
36// a circular dependency that manifests itself as different error messages depending
37// on which thing you export first. So, on a hunch, I reversed the order, and voila!
38
39class CDM_DECL CCompoundUnit
40{
41 // Define the vector type that holds our individual components of a CompoundUnit
42 typedef std::vector<CCompoundUnitElement> CUEVecType;
43
44public:
45 // Default ctor
46 CCompoundUnit() : m_dBigness(1.0),
47 m_CUD(nullptr),
48 m_bStaleBigness(true),
49 m_bStaleDimension(true),
50 m_bExplicitNonDBFlag(false),
51 m_bExplicitDBFlag(false),
52 m_bDBFlag(false)
53 {
54 };
55
56 // Construct directly from a unit string specification
57 CCompoundUnit(const std::string &unitString) : m_dBigness(1.0),
58 m_CUD(nullptr),
59 m_bStaleBigness(true),
60 m_bStaleDimension(true),
61 m_bExplicitNonDBFlag(false),
62 m_bExplicitDBFlag(false),
63 m_bDBFlag(false)
64 {
65 ParseString(unitString);
66 }
67
68 // Copy ctor
69 CCompoundUnit(const CCompoundUnit &src) : m_strUnit(src.m_strUnit),
70 m_dBigness(src.m_dBigness),
71 m_CUEVec(src.m_CUEVec),
72 m_bStaleBigness(src.m_bStaleBigness),
73 m_bStaleDimension(src.m_bStaleDimension),
74 m_bExplicitNonDBFlag(src.m_bExplicitNonDBFlag),
75 m_bExplicitDBFlag(src.m_bExplicitDBFlag),
76 m_bDBFlag(src.m_bDBFlag)
77 {
78 // In the initializer list, I'm assuming that initializing one vector with another
79 // copies the vector correctly. For the CUnitDimension object, we can't just
80 // initialize our pointer with the src's pointer, because each CCompoundUnit owns its
81 // CUnitDimension object, so we need to invoke the copy constructor on the
82 // CUnitDimension. Unfortunately, since the pointer might be nullptr, we can't simply
83 // do this in the initializer list.
84 if (src.m_CUD == nullptr)
85 {
86 m_CUD = nullptr;
87 }
88 else
89 {
90 m_CUD = new CUnitDimension(*src.m_CUD);
91 }
92 };
93
94 // dtor
96 {
97 if (m_CUD)
98 {
99 delete m_CUD;
100 }
101 };
102
103 // Obtain a dimension object that uniquely represents the dimensions of this
104 // compound unit in terms of fundamental quantities
105 const CUnitDimension *GetDimension() const;
106
107 // Obtain the relative magnitude of this compound unit in relation to
108 // an equivalently-dimensioned compound unit comprised of the base units
109 // of each fundamental quantity type. The bigness is the key to unit conversion.
110 // For any compound unit of a given dimension, the value of a quantity in that
111 // unit times the bigness of that unit is an invariant quantity.
112 const double &GetBigness() const;
113
114 // Obtain the bias of this compound unit. Biases denote additive offsets of one unit
115 // with respect to another unit of the same type. I am currently operating under the
116 // assumption that it is nonsensical to process biases in a compound unit consisting
117 // of more than one fundamental quantity type raised to the power of 1.0. If I come
118 // across a counter-example to this, I may have to re-think things, but for now, I am
119 // assuming that when multiplying a biased unit by other quantities, the bias must be
120 // irrelevant. Currently, the only quantity type that has biased units is temperature,
121 // and this principle seems to hold. E.g., J/degC means the same thing as J/degK even
122 // though there's a bias in converting from degC to degK.
123 double GetBias() const;
124
125 // We can do arithmetic with these, so overload a few operators
126 // But do *NOT* define operator+, operator-, or the assignment
127 // versions of these operators. You cannot "add" two CompoundUnit
128 // objects. You can add two *values* if they have equivalent
129 // compound unit objects, but the only operations that
130 // can be performed on a compound unit object itself are multiplication,
131 // division, and raising to a power.
133 {
134 if (this != &rhs)
135 {
136 m_dBigness = rhs.m_dBigness;
137 m_bDBFlag = rhs.m_bDBFlag;
138 m_bExplicitDBFlag = rhs.m_bExplicitDBFlag;
139 m_bExplicitNonDBFlag = rhs.m_bExplicitNonDBFlag;
140
141 // Vector objects should know how to deep-copy themselves
142 m_CUEVec = rhs.m_CUEVec;
143 // Free my dimension if I have one, whether it's stale or now
144 if (m_CUD)
145 {
146 delete m_CUD;
147 m_CUD = nullptr;
148 }
149 // We need to deep-copy the CUnitDimension, not copy pointers
150 // But only copy if RHS is not stale. No sense duplicating
151 // something that's stale. Also, if it's nullptr, it's stale, so
152 // no need for a separate nullptr check.
153 if (! rhs.m_bStaleDimension)
154 {
155 m_CUD = new CUnitDimension(*rhs.m_CUD);
156 }
157 m_bStaleBigness = rhs.m_bStaleBigness;
158 m_bStaleDimension = rhs.m_bStaleDimension;
159 }
160 return *this;
161 };
162
163 CCompoundUnit & operator*=(const CCompoundUnit &rhs);
164 CCompoundUnit & operator/=(const CCompoundUnit &rhs);
165
167 {
168 return CCompoundUnit(*this)*= rhs;
169 }
170
172 {
173 return CCompoundUnit(*this)/= rhs;
174 }
175
176 // There's no "raise to a power" operator, but this is the next best thing.
178
179 // Compare two CompoundUnits
180 // We don't care about how the units are precisely represented, whether
181 // one contains "J" and the other contains "kg m^2 s^-2", or "s^-2 m^2 kg"
182 // for that matter. We care about whether they are equivalent. And they are
183 // equivalent if and only if they are equivalent in both dimension and bigness.
184 bool operator==(const CCompoundUnit &rhs) const
185 {
186 return ( (*this->GetDimension() == *rhs.GetDimension()) &&
187 (this->GetBigness() == rhs.GetBigness()) &&
188 (this->GetBias() == rhs.GetBias()));
189 }
190
191 bool operator!=(const CCompoundUnit &rhs) const
192 {
193 return !(*this == rhs);
194 }
195
196 // Is this CompoundUnit dimensionally-compatible with a pre-defined
197 // quantity type?
198 bool IsOfType(int quantityTypeID);
199 bool IsOfType(const std::string &quantityName);
200
201 // Is this CompoundUnit dimensionless
202 bool IsDimensionless() const
203 {
204 return this->GetDimension()->IsDimensionless();
205 }
206
207 // Is this CompoundUnit in "decibel" mode
208 bool IsDecibel() const;
209
210 bool IsUnitEmpty() const
211 {
212 return this->m_strUnit.empty();
213 }
214
215 // Returns either 10 or 20, depending on whether this compound unit is of a
216 // Quantity Type that obeys the 20-Log-Rule
217 double GetDecibelLogScaleFactor() const;
218
220 {
221 // Make this explicitly decibel mode, in case we need to do a conversion to/from dB.
222 // We need explicit flags for each condition, so that with both of them off, the
223 // dB state is driven by the presence of a dB mode in the Compound Unit Elements
224 // (through the CompoundUnit expansion of a Unit Descriptor).
225 m_bExplicitDBFlag = true;
226 m_bExplicitNonDBFlag = false;
227 m_bDBFlag = true;
228 }
229
231 {
232 // Make this explicitly not-decibel mode
233 m_bExplicitDBFlag = false;
234 m_bExplicitNonDBFlag = true;
235 m_bDBFlag = false;
236 }
237
238 // Used for incrementally building up a CompoundUnit
239 CCompoundUnitElement & AddElement(const CCompoundUnitElement &);
240
241 // Erase everything
242 void clear()
243 {
244 if (m_CUD)
245 {
246 delete m_CUD;
247 m_CUD = nullptr;
248 }
249 m_dBigness = 1.0;
250 m_bStaleBigness = true;
251 m_bStaleDimension = true;
252 // The explicit dB flag is true if the CompoundUnit explicitly contained a "dB" token.
253 // The regular dB flag is true if either the explicit dB flag is true or if any of the
254 // Compound Unit Elements refer to Unit Descriptors whose Compound Unit expansion has
255 // its dB flag set
256 m_bExplicitDBFlag = false;
257 m_bDBFlag = false;
258 m_CUEVec.clear();
259 m_strUnit.clear();
260 }
261
262 // Build up my internals from a string specification.
263 void ParseString(const std::string &unitString);
264
265 std::string GetString() const {return m_strUnit;}
266
267 // Auxiliary output routine
268 std::ostream & PrintSelf(std::ostream &output) const;
269
270protected:
271 void BuildDimension() const;
272 void ComputeBigness() const;
273
274private:
275 // Many of these are declared "mutable" so that they can be changed on a "const"
276 // object. Ordinarily we don't let const objects be modified, but this allows us to
277 // declare certain member function "const" when the function is supposed to be "read-only",
278 // but in reality the "read" causes the update of cached values, and those must be
279 // declared "mutable"
280 mutable std::string m_strUnit;
281 mutable double m_dBigness;
284 mutable bool m_bStaleBigness;
285 mutable bool m_bStaleDimension;
287 mutable bool m_bExplicitDBFlag;
288 mutable bool m_bDBFlag;
289};
290
292{
293 // Construct a "C++ temporary" that we modify by raising to the power.
294 // It's the same principle that guides the implementation of operator*
295 // (see above) as described by Myers, except that there is no operator
296 // that corresponds to raising to a power, so we use an ordinary
297 // function instead.
298
299 return (CCompoundUnit(baseref)).Raise(exp);
300}
301
302inline CCompoundUnit sqrt(const CCompoundUnit &argref)
303{
304 return pow(argref, 0.5);
305}
306
307inline std::ostream & operator<<(std::ostream &out, const CCompoundUnit &ccu)
308{
309 //return ccu.PrintSelf(output);
310 out << ccu.GetString();
311 return out;
312}
313inline std::ostream& operator<< (std::ostream& out, const CCompoundUnit* ccu)
314{
315 if (ccu == nullptr)
316 out << "";
317 else
318 out << ccu->GetString();
319 return out;
320}
Definition: CompoundUnitElement.h:16
Definition: CompoundUnit.h:40
void DecibelModeOn()
Definition: CompoundUnit.h:219
void clear()
Definition: CompoundUnit.h:242
double m_dBigness
Definition: CompoundUnit.h:281
std::string m_strUnit
Definition: CompoundUnit.h:280
CCompoundUnit operator/(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:171
std::vector< CCompoundUnitElement > CUEVecType
Definition: CompoundUnit.h:42
CUnitDimension * m_CUD
Definition: CompoundUnit.h:283
bool m_bExplicitDBFlag
Definition: CompoundUnit.h:287
CUEVecType m_CUEVec
Definition: CompoundUnit.h:282
CCompoundUnit(const CCompoundUnit &src)
Definition: CompoundUnit.h:69
CCompoundUnit & Raise(CCompoundUnitElement::ExponentType)
Definition: CompoundUnit.cpp:594
bool m_bStaleBigness
Definition: CompoundUnit.h:284
const CUnitDimension * GetDimension() const
Definition: CompoundUnit.cpp:235
const double & GetBigness() const
Definition: CompoundUnit.cpp:251
void DecibelModeOff()
Definition: CompoundUnit.h:230
bool IsUnitEmpty() const
Definition: CompoundUnit.h:210
CCompoundUnit()
Definition: CompoundUnit.h:46
CCompoundUnit & operator=(const CCompoundUnit &rhs)
Definition: CompoundUnit.h:132
bool operator==(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:184
CCompoundUnit(const std::string &unitString)
Definition: CompoundUnit.h:57
virtual ~CCompoundUnit()
Definition: CompoundUnit.h:95
std::string GetString() const
Definition: CompoundUnit.h:265
bool m_bStaleDimension
Definition: CompoundUnit.h:285
bool m_bDBFlag
Definition: CompoundUnit.h:288
bool m_bExplicitNonDBFlag
Definition: CompoundUnit.h:286
CCompoundUnit operator*(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:166
bool operator!=(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:191
bool IsDimensionless() const
Definition: CompoundUnit.h:202
double GetBias() const
Definition: CompoundUnit.cpp:290
Definition: SnapValue.h:38
Definition: UnitDimension.h:22

Distributed under the Apache License, Version 2.0.

See accompanying NOTICE file for details.