]> granicus.if.org Git - icinga2/blob - lib/base/value-operators.cpp
Don't allow comparison of strings and numbers
[icinga2] / lib / base / value-operators.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/value.hpp"
21 #include "base/array.hpp"
22 #include "base/dictionary.hpp"
23 #include "base/utility.hpp"
24 #include "base/objectlock.hpp"
25 #include <boost/foreach.hpp>
26 #include <boost/lexical_cast.hpp>
27
28 using namespace icinga;
29
30 Value::operator double(void) const
31 {
32         const double *value = boost::get<double>(&m_Value);
33
34         if (value)
35                 return *value;
36
37         const bool *fvalue = boost::get<bool>(&m_Value);
38
39         if (fvalue)
40                 return *fvalue;
41
42         if (IsEmpty())
43                 return 0;
44
45         try {
46                 return boost::lexical_cast<double>(m_Value);
47         } catch (const std::exception&) {
48                 std::ostringstream msgbuf;
49                 msgbuf << "Can't convert '" << *this << "' to a floating point number.";
50                 BOOST_THROW_EXCEPTION(std::invalid_argument(msgbuf.str()));
51         }
52 }
53
54 Value::operator String(void) const
55 {
56         Object *object;
57         double integral, fractional;
58
59         switch (GetType()) {
60                 case ValueEmpty:
61                         return String();
62                 case ValueNumber:
63                         fractional = std::modf(boost::get<double>(m_Value), &integral);
64
65                         if (fractional != 0)
66                                 return boost::lexical_cast<std::string>(m_Value);
67                         else
68                                 return boost::lexical_cast<std::string>((long)integral);
69                 case ValueBoolean:
70                         if (boost::get<bool>(m_Value))
71                                 return "true";
72                         else
73                                 return "false";
74                 case ValueString:
75                         return boost::get<String>(m_Value);
76                 case ValueObject:
77                         object = boost::get<Object::Ptr>(m_Value).get();
78                         return object->ToString();
79                 default:
80                         BOOST_THROW_EXCEPTION(std::runtime_error("Unknown value type."));
81         }
82 }
83
84 std::ostream& icinga::operator<<(std::ostream& stream, const Value& value)
85 {
86         if (value.IsBoolean())
87                 stream << static_cast<int>(value);
88         else
89                 stream << static_cast<String>(value);
90
91         return stream;
92 }
93
94 std::istream& icinga::operator>>(std::istream& stream, Value& value)
95 {
96         String tstr;
97         stream >> tstr;
98         value = tstr;
99         return stream;
100 }
101
102 bool Value::operator==(bool rhs) const
103 {
104         return *this == Value(rhs);
105 }
106
107 bool Value::operator!=(bool rhs) const
108 {
109         return !(*this == rhs);
110 }
111
112 bool Value::operator==(int rhs) const
113 {
114         return *this == Value(rhs);
115 }
116
117 bool Value::operator!=(int rhs) const
118 {
119         return !(*this == rhs);
120 }
121
122 bool Value::operator==(double rhs) const
123 {
124         return *this == Value(rhs);
125 }
126
127 bool Value::operator!=(double rhs) const
128 {
129         return !(*this == rhs);
130 }
131
132 bool Value::operator==(const char *rhs) const
133 {
134         return static_cast<String>(*this) == rhs;
135 }
136
137 bool Value::operator!=(const char *rhs) const
138 {
139         return !(*this == rhs);
140 }
141
142 bool Value::operator==(const String& rhs) const
143 {
144         return static_cast<String>(*this) == rhs;
145 }
146
147 bool Value::operator!=(const String& rhs) const
148 {
149         return !(*this == rhs);
150 }
151
152 bool Value::operator==(const Value& rhs) const
153 {
154         if (IsNumber() && rhs.IsNumber())
155                 return Get<double>() == rhs.Get<double>();
156         else if ((IsBoolean() || IsNumber() || IsEmpty()) && (rhs.IsBoolean() || rhs.IsNumber() || rhs.IsEmpty()) && !(IsEmpty() && rhs.IsEmpty()))
157                 return static_cast<double>(*this) == static_cast<double>(rhs);
158
159         if (IsString() && rhs.IsString())
160                 return Get<String>() == rhs.Get<String>();
161         else if ((IsString() || IsEmpty()) && (rhs.IsString() || rhs.IsEmpty()) && !(IsEmpty() && rhs.IsEmpty()))
162                 return static_cast<String>(*this) == static_cast<String>(rhs);
163
164         if (IsEmpty() != rhs.IsEmpty())
165                 return false;
166
167         if (IsEmpty())
168                 return true;
169
170         if (IsObject() != rhs.IsObject())
171                 return false;
172
173         if (IsObject()) {
174                 if (IsObjectType<Array>() && rhs.IsObjectType<Array>()) {
175                         Array::Ptr arr1 = *this;
176                         Array::Ptr arr2 = rhs;
177
178                         if (arr1 == arr2)
179                                 return true;
180
181                         if (arr1->GetLength() != arr2->GetLength())
182                                 return false;
183
184                         for (Array::SizeType i = 0; i < arr1->GetLength(); i++) {
185                                 if (arr1->Get(i) != arr2->Get(i))
186                                         return false;
187                         }
188
189                         return true;
190                 }
191
192                 return Get<Object::Ptr>() == rhs.Get<Object::Ptr>();
193         }
194
195         return false;
196 }
197
198 bool Value::operator!=(const Value& rhs) const
199 {
200         return !(*this == rhs);
201 }
202
203 Value icinga::operator+(const Value& lhs, const char *rhs)
204 {
205         return lhs + Value(rhs);
206 }
207
208 Value icinga::operator+(const char *lhs, const Value& rhs)
209 {
210         return Value(lhs) + rhs;
211 }
212
213 Value icinga::operator+(const Value& lhs, const String& rhs)
214 {
215         return lhs + Value(rhs);
216 }
217
218 Value icinga::operator+(const String& lhs, const Value& rhs)
219 {
220         return Value(lhs) + rhs;
221 }
222
223 Value icinga::operator+(const Value& lhs, const Value& rhs)
224 {
225         if ((lhs.IsEmpty() || lhs.IsNumber()) && (rhs.IsEmpty() || rhs.IsNumber()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
226                 return static_cast<double>(lhs) + static_cast<double>(rhs);
227         if ((lhs.IsString() || lhs.IsEmpty() || lhs.IsNumber()) && (rhs.IsString() || rhs.IsEmpty() || rhs.IsNumber()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
228                 return static_cast<String>(lhs) + static_cast<String>(rhs);
229         else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
230                 return static_cast<double>(lhs) + static_cast<double>(rhs);
231         else if ((lhs.IsObjectType<Array>() || lhs.IsEmpty()) && (rhs.IsObjectType<Array>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty())) {
232                 Array::Ptr result = new Array();
233                 if (!lhs.IsEmpty())
234                         static_cast<Array::Ptr>(lhs)->CopyTo(result);
235                 if (!rhs.IsEmpty())
236                         static_cast<Array::Ptr>(rhs)->CopyTo(result);
237                 return result;
238         } else if ((lhs.IsObjectType<Dictionary>() || lhs.IsEmpty()) && (rhs.IsObjectType<Dictionary>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty())) {
239                 Dictionary::Ptr result = new Dictionary();
240                 if (!lhs.IsEmpty())
241                         static_cast<Dictionary::Ptr>(lhs)->CopyTo(result);
242                 if (!rhs.IsEmpty())
243                         static_cast<Dictionary::Ptr>(rhs)->CopyTo(result);
244                 return result;
245         } else {
246                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator + cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
247         }
248 }
249
250 Value icinga::operator+(const Value& lhs, double rhs)
251 {
252         return lhs + Value(rhs);
253 }
254
255 Value icinga::operator+(double lhs, const Value& rhs)
256 {
257         return Value(lhs) + rhs;
258 }
259
260 Value icinga::operator+(const Value& lhs, int rhs)
261 {
262         return lhs + Value(rhs);
263 }
264
265 Value icinga::operator+(int lhs, const Value& rhs)
266 {
267         return Value(lhs) + rhs;
268 }
269
270 Value icinga::operator-(const Value& lhs, const Value& rhs)
271 {
272         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
273                 return static_cast<double>(lhs) - static_cast<double>(rhs);
274         else if ((lhs.IsObjectType<Array>() || lhs.IsEmpty()) && (rhs.IsObjectType<Array>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty())) {
275                 if (lhs.IsEmpty())
276                         return new Array();
277
278                 Array::Ptr result = new Array();
279                 Array::Ptr left = lhs;
280                 Array::Ptr right = rhs;
281
282                 ObjectLock olock(left);
283                 BOOST_FOREACH(const Value& lv, left) {
284                         bool found = false;
285                         ObjectLock xlock(right);
286                         BOOST_FOREACH(const Value& rv, right) {
287                                 if (lv == rv) {
288                                         found = true;
289                                         break;
290                                 }
291                         }
292
293                         if (found)
294                                 continue;
295
296                         result->Add(lv);
297                 }
298
299                 return result;
300         } else
301                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator - cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
302 }
303
304 Value icinga::operator-(const Value& lhs, double rhs)
305 {
306         return lhs - Value(rhs);
307 }
308
309 Value icinga::operator-(double lhs, const Value& rhs)
310 {
311         return Value(lhs) - rhs;
312 }
313
314 Value icinga::operator-(const Value& lhs, int rhs)
315 {
316         return lhs - Value(rhs);
317 }
318
319 Value icinga::operator-(int lhs, const Value& rhs)
320 {
321         return Value(lhs) - rhs;
322 }
323
324 Value icinga::operator*(const Value& lhs, const Value& rhs)
325 {
326         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
327                 return static_cast<double>(lhs) * static_cast<double>(rhs);
328         else
329                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator * cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
330 }
331
332 Value icinga::operator*(const Value& lhs, double rhs)
333 {
334         return lhs * Value(rhs);
335 }
336
337 Value icinga::operator*(double lhs, const Value& rhs)
338 {
339         return Value(lhs) * rhs;
340 }
341
342 Value icinga::operator*(const Value& lhs, int rhs)
343 {
344         return lhs * Value(rhs);
345 }
346
347 Value icinga::operator*(int lhs, const Value& rhs)
348 {
349         return Value(lhs) * rhs;
350 }
351
352 Value icinga::operator/(const Value& lhs, const Value& rhs)
353 {
354         if (rhs.IsEmpty())
355                 BOOST_THROW_EXCEPTION(std::invalid_argument("Right-hand side argument for operator / is Empty."));
356         else if ((lhs.IsEmpty() || lhs.IsNumber()) && rhs.IsNumber()) {
357                 if (static_cast<double>(rhs) == 0)
358                         BOOST_THROW_EXCEPTION(std::invalid_argument("Right-hand side argument for operator / is 0."));
359
360                 return static_cast<double>(lhs) / static_cast<double>(rhs);
361         } else
362                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator / cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
363 }
364
365 Value icinga::operator/(const Value& lhs, double rhs)
366 {
367         return lhs / Value(rhs);
368 }
369
370 Value icinga::operator/(double lhs, const Value& rhs)
371 {
372         return Value(lhs) / rhs;
373 }
374
375 Value icinga::operator/(const Value& lhs, int rhs)
376 {
377         return lhs / Value(rhs);
378 }
379
380 Value icinga::operator/(int lhs, const Value& rhs)
381 {
382         return Value(lhs) / rhs;
383 }
384
385 Value icinga::operator%(const Value& lhs, const Value& rhs)
386 {
387         if (rhs.IsEmpty())
388                 BOOST_THROW_EXCEPTION(std::invalid_argument("Right-hand side argument for operator % is Empty."));
389         else if ((rhs.IsNumber() || lhs.IsNumber()) && rhs.IsNumber()) {
390                 if (static_cast<double>(rhs) == 0)
391                         BOOST_THROW_EXCEPTION(std::invalid_argument("Right-hand side argument for operator % is 0."));
392
393                 return static_cast<int>(lhs) % static_cast<int>(rhs);
394         } else
395                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator % cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
396 }
397
398 Value icinga::operator%(const Value& lhs, double rhs)
399 {
400         return lhs % Value(rhs);
401 }
402
403 Value icinga::operator%(double lhs, const Value& rhs)
404 {
405         return Value(lhs) % rhs;
406 }
407
408 Value icinga::operator%(const Value& lhs, int rhs)
409 {
410         return lhs % Value(rhs);
411 }
412
413 Value icinga::operator%(int lhs, const Value& rhs)
414 {
415         return Value(lhs) % rhs;
416 }
417
418 Value icinga::operator^(const Value& lhs, const Value& rhs)
419 {
420         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
421                 return static_cast<int>(lhs) ^ static_cast<int>(rhs);
422         else
423                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator & cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
424 }
425
426 Value icinga::operator^(const Value& lhs, double rhs)
427 {
428         return lhs & Value(rhs);
429 }
430
431 Value icinga::operator^(double lhs, const Value& rhs)
432 {
433         return Value(lhs) & rhs;
434 }
435
436 Value icinga::operator^(const Value& lhs, int rhs)
437 {
438         return lhs & Value(rhs);
439 }
440
441 Value icinga::operator^(int lhs, const Value& rhs)
442 {
443         return Value(lhs) & rhs;
444 }
445
446 Value icinga::operator&(const Value& lhs, const Value& rhs)
447 {
448         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
449                 return static_cast<int>(lhs) & static_cast<int>(rhs);
450         else
451                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator & cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
452 }
453
454 Value icinga::operator&(const Value& lhs, double rhs)
455 {
456         return lhs & Value(rhs);
457 }
458
459 Value icinga::operator&(double lhs, const Value& rhs)
460 {
461         return Value(lhs) & rhs;
462 }
463
464 Value icinga::operator&(const Value& lhs, int rhs)
465 {
466         return lhs & Value(rhs);
467 }
468
469 Value icinga::operator&(int lhs, const Value& rhs)
470 {
471         return Value(lhs) & rhs;
472 }
473
474 Value icinga::operator|(const Value& lhs, const Value& rhs)
475 {
476         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
477                 return static_cast<int>(lhs) | static_cast<int>(rhs);
478         else
479                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator | cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
480 }
481
482 Value icinga::operator|(const Value& lhs, double rhs)
483 {
484         return lhs | Value(rhs);
485 }
486
487 Value icinga::operator|(double lhs, const Value& rhs)
488 {
489         return Value(lhs) | rhs;
490 }
491
492 Value icinga::operator|(const Value& lhs, int rhs)
493 {
494         return lhs | Value(rhs);
495 }
496
497 Value icinga::operator|(int lhs, const Value& rhs)
498 {
499         return Value(lhs) | rhs;
500 }
501
502 Value icinga::operator<<(const Value& lhs, const Value& rhs)
503 {
504         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
505                 return static_cast<int>(lhs) << static_cast<int>(rhs);
506         else
507                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator << cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
508 }
509
510 Value icinga::operator<<(const Value& lhs, double rhs)
511 {
512         return lhs << Value(rhs);
513 }
514
515 Value icinga::operator<<(double lhs, const Value& rhs)
516 {
517         return Value(lhs) << rhs;
518 }
519
520 Value icinga::operator<<(const Value& lhs, int rhs)
521 {
522         return lhs << Value(rhs);
523 }
524
525 Value icinga::operator<<(int lhs, const Value& rhs)
526 {
527         return Value(lhs) << rhs;
528 }
529
530 Value icinga::operator>>(const Value& lhs, const Value& rhs)
531 {
532         if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
533                 return static_cast<int>(lhs) >> static_cast<int>(rhs);
534         else
535                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator >> cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
536 }
537
538 Value icinga::operator>>(const Value& lhs, double rhs)
539 {
540         return lhs >> Value(rhs);
541 }
542
543 Value icinga::operator>>(double lhs, const Value& rhs)
544 {
545         return Value(lhs) >> rhs;
546 }
547
548 Value icinga::operator>>(const Value& lhs, int rhs)
549 {
550         return lhs >> Value(rhs);
551 }
552
553 Value icinga::operator>>(int lhs, const Value& rhs)
554 {
555         return Value(lhs) >> rhs;
556 }
557
558 bool icinga::operator<(const Value& lhs, const Value& rhs)
559 {
560         if (lhs.IsString() && rhs.IsString())
561                 return static_cast<String>(lhs) < static_cast<String>(rhs);
562         else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
563                 return static_cast<double>(lhs) < static_cast<double>(rhs);
564         else
565                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator < cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
566 }
567
568 bool icinga::operator<(const Value& lhs, double rhs)
569 {
570         return lhs < Value(rhs);
571 }
572
573 bool icinga::operator<(double lhs, const Value& rhs)
574 {
575         return Value(lhs) < rhs;
576 }
577
578 bool icinga::operator<(const Value& lhs, int rhs)
579 {
580         return lhs < Value(rhs);
581 }
582
583 bool icinga::operator<(int lhs, const Value& rhs)
584 {
585         return Value(lhs) < rhs;
586 }
587
588 bool icinga::operator>(const Value& lhs, const Value& rhs)
589 {
590         if (lhs.IsString() && rhs.IsString())
591                 return static_cast<String>(lhs) > static_cast<String>(rhs);
592         else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
593                 return static_cast<double>(lhs) > static_cast<double>(rhs);
594         else
595                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator > cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
596 }
597
598 bool icinga::operator>(const Value& lhs, double rhs)
599 {
600         return lhs > Value(rhs);
601 }
602
603 bool icinga::operator>(double lhs, const Value& rhs)
604 {
605         return Value(lhs) > rhs;
606 }
607
608 bool icinga::operator>(const Value& lhs, int rhs)
609 {
610         return lhs > Value(rhs);
611 }
612
613 bool icinga::operator>(int lhs, const Value& rhs)
614 {
615         return Value(lhs) > rhs;
616 }
617
618 bool icinga::operator<=(const Value& lhs, const Value& rhs)
619 {
620         if (lhs.IsString() && rhs.IsString())
621                 return static_cast<String>(lhs) <= static_cast<String>(rhs);
622         else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
623                 return static_cast<double>(lhs) <= static_cast<double>(rhs);
624         else
625                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator <= cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
626 }
627
628 bool icinga::operator<=(const Value& lhs, double rhs)
629 {
630         return lhs <= Value(rhs);
631 }
632
633 bool icinga::operator<=(double lhs, const Value& rhs)
634 {
635         return Value(lhs) <= rhs;
636 }
637
638 bool icinga::operator<=(const Value& lhs, int rhs)
639 {
640         return lhs <= Value(rhs);
641 }
642
643 bool icinga::operator<=(int lhs, const Value& rhs)
644 {
645         return Value(lhs) <= rhs;
646 }
647
648 bool icinga::operator>=(const Value& lhs, const Value& rhs)
649 {
650         if (lhs.IsString() && rhs.IsString())
651                 return static_cast<String>(lhs) >= static_cast<String>(rhs);
652         else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
653                 return static_cast<double>(lhs) >= static_cast<double>(rhs);
654         else
655                 BOOST_THROW_EXCEPTION(std::invalid_argument("Operator >= cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
656 }
657
658 bool icinga::operator>=(const Value& lhs, double rhs)
659 {
660         return lhs >= Value(rhs);
661 }
662
663 bool icinga::operator>=(double lhs, const Value& rhs)
664 {
665         return Value(lhs) >= rhs;
666 }
667
668 bool icinga::operator>=(const Value& lhs, int rhs)
669 {
670         return lhs >= Value(rhs);
671 }
672
673 bool icinga::operator>=(int lhs, const Value& rhs)
674 {
675         return Value(lhs) >= rhs;
676 }