1 /* 2 * Copyright (c) 2015, Vsevolod Stakhov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #pragma once 26 #include <string> 27 #include <vector> 28 #include <map> 29 #include <set> 30 #include <memory> 31 #include <iostream> 32 #include <tuple> 33 34 #include "ucl.h" 35 36 // C++11 API inspired by json11: https://github.com/dropbox/json11/ 37 38 namespace ucl { 39 40 struct ucl_map_construct_t {}; 41 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t(); 42 struct ucl_array_construct_t {}; 43 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t(); 44 45 class Ucl final { 46 private: 47 struct ucl_deleter { 48 void operator()(ucl_object_t *obj) 49 { 50 ucl_object_unref(obj); 51 } 52 }; 53 54 static int 55 append_char(unsigned char c, size_t nchars, void *ud) 56 { 57 std::string *out = reinterpret_cast<std::string *>(ud); 58 59 out->append(nchars, (char) c); 60 61 return nchars; 62 } 63 static int 64 append_len(unsigned const char *str, size_t len, void *ud) 65 { 66 std::string *out = reinterpret_cast<std::string *>(ud); 67 68 out->append((const char *) str, len); 69 70 return len; 71 } 72 static int 73 append_int(int64_t elt, void *ud) 74 { 75 std::string *out = reinterpret_cast<std::string *>(ud); 76 auto nstr = std::to_string(elt); 77 78 out->append(nstr); 79 80 return nstr.size(); 81 } 82 static int 83 append_double(double elt, void *ud) 84 { 85 std::string *out = reinterpret_cast<std::string *>(ud); 86 auto nstr = std::to_string(elt); 87 88 out->append(nstr); 89 90 return nstr.size(); 91 } 92 93 static struct ucl_emitter_functions default_emit_funcs() 94 { 95 struct ucl_emitter_functions func = { 96 Ucl::append_char, 97 Ucl::append_len, 98 Ucl::append_int, 99 Ucl::append_double, 100 nullptr, 101 nullptr}; 102 103 return func; 104 }; 105 106 static bool ucl_variable_getter(const unsigned char *data, size_t len, 107 unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void *ud) 108 { 109 *need_free = false; 110 111 auto vars = reinterpret_cast<std::set<std::string> *>(ud); 112 if (vars && data && len != 0) { 113 vars->emplace(data, data + len); 114 } 115 return false; 116 } 117 118 static bool ucl_variable_replacer(const unsigned char *data, size_t len, 119 unsigned char **replace, size_t *replace_len, bool *need_free, void *ud) 120 { 121 *need_free = false; 122 123 auto replacer = reinterpret_cast<variable_replacer *>(ud); 124 if (!replacer) { 125 return false; 126 } 127 128 std::string var_name(data, data + len); 129 if (!replacer->is_variable(var_name)) { 130 return false; 131 } 132 133 std::string var_value = replacer->replace(var_name); 134 if (var_value.empty()) { 135 return false; 136 } 137 138 *replace = (unsigned char *) UCL_ALLOC(var_value.size()); 139 memcpy(*replace, var_value.data(), var_value.size()); 140 141 *replace_len = var_value.size(); 142 *need_free = true; 143 144 return true; 145 } 146 147 template<typename C, typename P> 148 static Ucl parse_with_strategy_function(C config_func, P parse_func, std::string &err) 149 { 150 auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); 151 152 config_func(parser); 153 154 if (!parse_func(parser)) { 155 const char *error = ucl_parser_get_error(parser);//Assigning here without checking result first causes a 156 if (error != NULL) err.assign(error); // crash if ucl_parser_get_error returns NULL 157 ucl_parser_free(parser); 158 159 return nullptr; 160 } 161 162 auto obj = ucl_parser_get_object(parser); 163 ucl_parser_free(parser); 164 165 // Obj will handle ownership 166 return Ucl(obj); 167 } 168 169 std::unique_ptr<ucl_object_t, ucl_deleter> obj; 170 171 public: 172 struct macro_handler_s { 173 ucl_macro_handler handler; 174 ucl_context_macro_handler ctx_handler; 175 }; 176 177 struct macro_userdata_s { 178 ucl_parser *parser; 179 void *userdata; 180 }; 181 182 class const_iterator { 183 private: 184 struct ucl_iter_deleter { 185 void operator()(ucl_object_iter_t it) 186 { 187 ucl_object_iterate_free(it); 188 } 189 }; 190 std::shared_ptr<void> it; 191 std::unique_ptr<Ucl> cur; 192 193 public: 194 typedef std::forward_iterator_tag iterator_category; 195 196 const_iterator(const Ucl &obj) 197 { 198 it = std::shared_ptr<void>(ucl_object_iterate_new(obj.obj.get()), 199 ucl_iter_deleter()); 200 cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true))); 201 if (!cur->obj) { 202 it.reset(); 203 cur.reset(); 204 } 205 } 206 207 const_iterator() 208 { 209 } 210 const_iterator(const const_iterator &other) = delete; 211 const_iterator(const_iterator &&other) = default; 212 ~const_iterator() 213 { 214 } 215 216 const_iterator &operator=(const const_iterator &other) = delete; 217 const_iterator &operator=(const_iterator &&other) = default; 218 219 bool operator==(const const_iterator &other) const 220 { 221 if (cur && other.cur) { 222 return cur->obj.get() == other.cur->obj.get(); 223 } 224 225 return !cur && !other.cur; 226 } 227 228 bool operator!=(const const_iterator &other) const 229 { 230 return !(*this == other); 231 } 232 233 const_iterator &operator++() 234 { 235 if (it) { 236 cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true))); 237 } 238 239 if (cur && !cur->obj) { 240 it.reset(); 241 cur.reset(); 242 } 243 244 return *this; 245 } 246 247 const Ucl &operator*() const 248 { 249 return *cur; 250 } 251 const Ucl *operator->() const 252 { 253 return cur.get(); 254 } 255 }; 256 257 struct variable_replacer { 258 virtual ~variable_replacer() 259 { 260 } 261 262 virtual bool is_variable(const std::string &str) const 263 { 264 return !str.empty(); 265 } 266 267 virtual std::string replace(const std::string &var) const = 0; 268 }; 269 270 // We grab ownership if get non-const ucl_object_t 271 Ucl(ucl_object_t *other) 272 { 273 obj.reset(other); 274 } 275 276 // Shared ownership 277 Ucl(const ucl_object_t *other) 278 { 279 obj.reset(ucl_object_ref(other)); 280 } 281 282 Ucl(const Ucl &other) 283 { 284 obj.reset(ucl_object_ref(other.obj.get())); 285 } 286 287 Ucl(Ucl &&other) 288 { 289 obj.swap(other.obj); 290 } 291 292 Ucl() noexcept 293 { 294 obj.reset(ucl_object_typed_new(UCL_NULL)); 295 } 296 Ucl(std::nullptr_t) noexcept 297 { 298 obj.reset(ucl_object_typed_new(UCL_NULL)); 299 } 300 Ucl(double value) 301 { 302 obj.reset(ucl_object_typed_new(UCL_FLOAT)); 303 obj->value.dv = value; 304 } 305 Ucl(int64_t value) 306 { 307 obj.reset(ucl_object_typed_new(UCL_INT)); 308 obj->value.iv = value; 309 } 310 Ucl(bool value) 311 { 312 obj.reset(ucl_object_typed_new(UCL_BOOLEAN)); 313 obj->value.iv = static_cast<int64_t>(value); 314 } 315 Ucl(const std::string &value) 316 { 317 obj.reset(ucl_object_fromstring_common(value.data(), value.size(), 318 UCL_STRING_RAW)); 319 } 320 Ucl(const char *value) 321 { 322 obj.reset(ucl_object_fromstring_common(value, 0, UCL_STRING_RAW)); 323 } 324 325 // Implicit constructor: anything with a to_json() function. 326 template<class T, class = decltype(&T::to_ucl)> 327 Ucl(const T &t) 328 : Ucl(t.to_ucl()) 329 { 330 } 331 332 // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) 333 template<class M, typename std::enable_if< 334 std::is_constructible<std::string, typename M::key_type>::value && std::is_constructible<Ucl, typename M::mapped_type>::value, 335 int>::type = 0> 336 Ucl(const M &m) 337 { 338 obj.reset(ucl_object_typed_new(UCL_OBJECT)); 339 auto cobj = obj.get(); 340 341 for (const auto &e: m) { 342 ucl_object_insert_key(cobj, ucl_object_ref(e.second.obj.get()), 343 e.first.data(), e.first.size(), true); 344 } 345 } 346 347 // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) 348 template<class V, typename std::enable_if< 349 std::is_constructible<Ucl, typename V::value_type>::value, 350 int>::type = 0> 351 Ucl(const V &v) 352 { 353 obj.reset(ucl_object_typed_new(UCL_ARRAY)); 354 auto cobj = obj.get(); 355 356 for (const auto &e: v) { 357 ucl_array_append(cobj, ucl_object_ref(e.obj.get())); 358 } 359 } 360 361 ucl_type_t type() const 362 { 363 if (obj) { 364 return ucl_object_type(obj.get()); 365 } 366 return UCL_NULL; 367 } 368 369 std::string key() const 370 { 371 std::string res; 372 373 if (obj->key) { 374 res.assign(obj->key, obj->keylen); 375 } 376 377 return res; 378 } 379 380 double number_value(const double default_val = 0.0) const 381 { 382 double res; 383 384 if (ucl_object_todouble_safe(obj.get(), &res)) { 385 return res; 386 } 387 388 return default_val; 389 } 390 391 int64_t int_value(const int64_t default_val = 0) const 392 { 393 int64_t res; 394 395 if (ucl_object_toint_safe(obj.get(), &res)) { 396 return res; 397 } 398 399 return default_val; 400 } 401 402 bool bool_value(const bool default_val = false) const 403 { 404 bool res; 405 406 if (ucl_object_toboolean_safe(obj.get(), &res)) { 407 return res; 408 } 409 410 return default_val; 411 } 412 413 std::string string_value(const std::string &default_val = "") const 414 { 415 const char *res = nullptr; 416 417 if (ucl_object_tostring_safe(obj.get(), &res)) { 418 return res; 419 } 420 421 return default_val; 422 } 423 424 std::string forced_string_value () const 425 { 426 return ucl_object_tostring_forced(obj.get()); 427 } 428 429 size_t size() const 430 { 431 if (type() == UCL_ARRAY) { 432 return ucl_array_size(obj.get()); 433 } 434 435 return 0; 436 } 437 438 Ucl at(size_t i) const 439 { 440 if (type() == UCL_ARRAY) { 441 return Ucl(ucl_array_find_index(obj.get(), i)); 442 } 443 444 return Ucl(nullptr); 445 } 446 447 Ucl lookup(const std::string &key) const 448 { 449 if (type() == UCL_OBJECT) { 450 return Ucl(ucl_object_lookup_len(obj.get(), 451 key.data(), key.size())); 452 } 453 454 return Ucl(nullptr); 455 } 456 457 inline Ucl operator[](size_t i) const 458 { 459 return at(i); 460 } 461 462 inline Ucl operator[](const std::string &key) const 463 { 464 return lookup(key); 465 } 466 // Serialize. 467 void dump(std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const 468 { 469 struct ucl_emitter_functions cbdata; 470 471 cbdata = Ucl::default_emit_funcs(); 472 cbdata.ud = reinterpret_cast<void *>(&out); 473 474 ucl_object_emit_full(obj.get(), type, &cbdata, nullptr); 475 } 476 477 std::string dump(ucl_emitter_t type = UCL_EMIT_JSON) const 478 { 479 std::string out; 480 481 dump(out, type); 482 483 return out; 484 } 485 486 static Ucl parse(const std::string &in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 487 { 488 return parse(in, std::map<std::string, std::string>(), err, duplicate_strategy); 489 } 490 491 static Ucl parse(const std::string &in, const std::map<std::string, std::string> &vars, 492 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 493 { 494 std::vector<std::tuple<std::string, macro_handler_s, void *>> emptyVector; 495 return parse(in, vars, emptyVector, err, duplicate_strategy); 496 } 497 498 //Macro handler will receive a macro_userdata_s as void *ud 499 static Ucl parse(const std::string &in, 500 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 501 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 502 { 503 return parse(in, std::map<std::string, std::string>(), macros, err, duplicate_strategy); 504 } 505 506 //Macro handler will receive a macro_userdata_s as void *ud 507 static Ucl parse(const std::string &in, const std::map<std::string, std::string> &vars, 508 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 509 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 510 { 511 //Preserve macro_userdata_s memory for later use in parse_with_strategy_function() 512 std::vector<macro_userdata_s> userdata_list; 513 userdata_list.reserve(macros.size()); 514 auto config_func = [&userdata_list, &vars, ¯os](ucl_parser *parser) { 515 for (const auto &item: vars) { 516 ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str()); 517 } 518 for (auto ¯o: macros) { 519 userdata_list.push_back({parser, std::get<2>(macro)}); 520 if (std::get<1>(macro).handler != NULL) { 521 ucl_parser_register_macro(parser, 522 std::get<0>(macro).c_str(), 523 std::get<1>(macro).handler, 524 reinterpret_cast<void *>(&userdata_list.back())); 525 } 526 else if (std::get<1>(macro).ctx_handler != NULL) { 527 ucl_parser_register_context_macro(parser, 528 std::get<0>(macro).c_str(), 529 std::get<1>(macro).ctx_handler, 530 reinterpret_cast<void *>(&userdata_list.back())); 531 } 532 } 533 }; 534 535 auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool { 536 return ucl_parser_add_chunk_full(parser, 537 (unsigned char *) in.data(), 538 in.size(), 539 (unsigned int) ucl_parser_get_default_priority(parser), 540 duplicate_strategy, 541 UCL_PARSE_UCL); 542 }; 543 544 return parse_with_strategy_function(config_func, parse_func, err); 545 } 546 547 static Ucl parse(const std::string &in, const variable_replacer &replacer, 548 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 549 { 550 std::vector<std::tuple<std::string, macro_handler_s, void *>> emptyVector; 551 return parse(in, replacer, emptyVector, err, duplicate_strategy); 552 } 553 554 //Macro handler will receive a macro_userdata_s as void *ud 555 static Ucl parse(const std::string &in, const variable_replacer &replacer, 556 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 557 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 558 { 559 //Preserve macro_userdata_s memory for later use in parse_with_strategy_function() 560 std::vector<macro_userdata_s> userdata_list; 561 userdata_list.reserve(macros.size()); 562 auto config_func = [&userdata_list, &replacer, ¯os](ucl_parser *parser) { 563 ucl_parser_set_variables_handler(parser, ucl_variable_replacer, &const_cast<variable_replacer &>(replacer)); 564 for (auto ¯o: macros) { 565 userdata_list.push_back({parser, std::get<2>(macro)}); 566 if (std::get<1>(macro).handler != NULL) { 567 ucl_parser_register_macro(parser, 568 std::get<0>(macro).c_str(), 569 std::get<1>(macro).handler, 570 reinterpret_cast<void *>(&userdata_list.back())); 571 } 572 else if (std::get<1>(macro).ctx_handler != NULL) { 573 ucl_parser_register_context_macro(parser, 574 std::get<0>(macro).c_str(), 575 std::get<1>(macro).ctx_handler, 576 reinterpret_cast<void *>(&userdata_list.back())); 577 } 578 } 579 }; 580 581 auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool { 582 return ucl_parser_add_chunk_full(parser, 583 (unsigned char *) in.data(), 584 in.size(), 585 (unsigned int) ucl_parser_get_default_priority(parser), 586 duplicate_strategy, 587 UCL_PARSE_UCL); 588 }; 589 590 return parse_with_strategy_function(config_func, parse_func, err); 591 } 592 593 static Ucl parse(const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 594 { 595 return parse(in, std::map<std::string, std::string>(), err, duplicate_strategy); 596 } 597 598 static Ucl parse(const char *in, const std::map<std::string, std::string> &vars, std::string &err) 599 { 600 if (!in) { 601 err = "null input"; 602 return nullptr; 603 } 604 return parse(std::string(in), vars, err); 605 } 606 607 //Macro handler will receive a macro_userdata_s as void *ud 608 static Ucl parse(const char *in, 609 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 610 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 611 { 612 return parse(in, std::map<std::string, std::string>(), macros, err, duplicate_strategy); 613 } 614 615 //Macro handler will receive a macro_userdata_s as void *ud 616 static Ucl parse(const char *in, const std::map<std::string, std::string> &vars, 617 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 618 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 619 { 620 if (!in) { 621 err = "null input"; 622 return nullptr; 623 } 624 return parse(std::string(in), vars, macros, err, duplicate_strategy); 625 } 626 627 static Ucl parse(const char *in, const variable_replacer &replacer, 628 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 629 { 630 if (!in) { 631 err = "null input"; 632 return nullptr; 633 } 634 return parse(std::string(in), replacer, err, duplicate_strategy); 635 } 636 637 //Macro handler will receive a macro_userdata_s as void *ud 638 static Ucl parse(const char *in, const variable_replacer &replacer, 639 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> ¯os, 640 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND) 641 { 642 if (!in) { 643 err = "null input"; 644 return nullptr; 645 } 646 return parse(std::string(in), replacer, macros, err, duplicate_strategy); 647 } 648 649 static Ucl parse_from_file(const std::string &filename, std::string &err) 650 { 651 return parse_from_file(filename, std::map<std::string, std::string>(), err); 652 } 653 654 static Ucl parse_from_file(const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err) 655 { 656 auto config_func = [&vars](ucl_parser *parser) { 657 for (const auto &item: vars) { 658 ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str()); 659 } 660 }; 661 662 auto parse_func = [&filename](ucl_parser *parser) { 663 return ucl_parser_add_file(parser, filename.c_str()); 664 }; 665 666 return parse_with_strategy_function(config_func, parse_func, err); 667 } 668 669 static Ucl parse_from_file(const std::string &filename, const variable_replacer &replacer, std::string &err) 670 { 671 auto config_func = [&replacer](ucl_parser *parser) { 672 ucl_parser_set_variables_handler(parser, ucl_variable_replacer, 673 &const_cast<variable_replacer &>(replacer)); 674 }; 675 676 auto parse_func = [&filename](ucl_parser *parser) { 677 return ucl_parser_add_file(parser, filename.c_str()); 678 }; 679 680 return parse_with_strategy_function(config_func, parse_func, err); 681 } 682 683 static std::vector<std::string> find_variable(const std::string &in) 684 { 685 auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); 686 687 std::set<std::string> vars; 688 ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars); 689 ucl_parser_add_chunk(parser, (const unsigned char *) in.data(), in.size()); 690 ucl_parser_free(parser); 691 692 std::vector<std::string> result; 693 std::move(vars.begin(), vars.end(), std::back_inserter(result)); 694 return result; 695 } 696 697 static std::vector<std::string> find_variable(const char *in) 698 { 699 if (!in) { 700 return std::vector<std::string>(); 701 } 702 return find_variable(std::string(in)); 703 } 704 705 static std::vector<std::string> find_variable_from_file(const std::string &filename) 706 { 707 auto parser = ucl_parser_new(UCL_PARSER_DEFAULT); 708 709 std::set<std::string> vars; 710 ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars); 711 ucl_parser_add_file(parser, filename.c_str()); 712 ucl_parser_free(parser); 713 714 std::vector<std::string> result; 715 std::move(vars.begin(), vars.end(), std::back_inserter(result)); 716 return result; 717 } 718 719 Ucl &operator=(Ucl rhs) 720 { 721 obj.swap(rhs.obj); 722 return *this; 723 } 724 725 bool operator==(const Ucl &rhs) const 726 { 727 return ucl_object_compare(obj.get(), rhs.obj.get()) == 0; 728 } 729 bool operator<(const Ucl &rhs) const 730 { 731 return ucl_object_compare(obj.get(), rhs.obj.get()) < 0; 732 } 733 bool operator!=(const Ucl &rhs) const 734 { 735 return !(*this == rhs); 736 } 737 bool operator<=(const Ucl &rhs) const 738 { 739 return !(rhs < *this); 740 } 741 bool operator>(const Ucl &rhs) const 742 { 743 return (rhs < *this); 744 } 745 bool operator>=(const Ucl &rhs) const 746 { 747 return !(*this < rhs); 748 } 749 750 explicit operator bool() const 751 { 752 if (!obj || type() == UCL_NULL) { 753 return false; 754 } 755 756 if (type() == UCL_BOOLEAN) { 757 return bool_value(); 758 } 759 760 return true; 761 } 762 763 const_iterator begin() const 764 { 765 return const_iterator(*this); 766 } 767 const_iterator cbegin() const 768 { 769 return const_iterator(*this); 770 } 771 const_iterator end() const 772 { 773 return const_iterator(); 774 } 775 const_iterator cend() const 776 { 777 return const_iterator(); 778 } 779 }; 780 781 };// namespace ucl 782