├── .gitignore ├── Makefile ├── README.md ├── animal.h ├── calc.test.cc ├── intersects.h ├── intersects.test.cc ├── lick.cc ├── lick.h ├── shapes1.h ├── shapes2.h ├── shapes3.h ├── shapes4.h ├── speed.test.cc ├── transport.h ├── transport_animal.h ├── transport_animal.test.cc ├── val.h ├── variant.h └── variant.test.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.a 4 | *.lib 5 | *.out 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: ../out/variant.test 2 | ../out/variant.test 3 | 4 | ../out/variant.test: ../out/variant.test.o ../out/lick.o 5 | mkdir -p ../out; clang++ -g -o ../out/variant.test ../out/variant.test.o ../out/lick.o 6 | 7 | ../out/variant.test.o: variant.test.cc variant.h lick.h 8 | mkdir -p ../out; clang++ -std=c++1y -c -g -Wall -Wextra -o ../out/variant.test.o variant.test.cc 9 | 10 | ../out/lick.o: lick.cc lick.h 11 | mkdir -p ../out; clang++ -std=c++1y -c -g -Wall -Wextra -o ../out/lick.o lick.cc 12 | 13 | clean: 14 | rm -r ../out 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cppcon14 2 | 3 | source code for CppCon 2014 talk on polymorphic unions 4 | 5 | ## Clone/Build 6 | 7 | ```bash 8 | mkdir cppcon14 9 | cd cppcon14 10 | git clone git@github.com:JasonL9000/cppcon14.git src 11 | cd src 12 | make 13 | ``` 14 | -------------------------------------------------------------------------------- /animal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "variant.h" 4 | 5 | // --------------------------------------------------------------------------- 6 | // "dog.h" 7 | 8 | struct dog_t { 9 | // stuff about barking, licking people, and chasing cars 10 | }; 11 | // --------------------------------------------------------------------------- 12 | 13 | 14 | // --------------------------------------------------------------------------- 15 | // "cat.h" 16 | 17 | struct cat_t { 18 | // stuff about meowing and ignoring people 19 | }; 20 | // --------------------------------------------------------------------------- 21 | 22 | 23 | // --------------------------------------------------------------------------- 24 | // "horse.h" 25 | 26 | struct horse_t { 27 | // stuff about neighing, running fast, and carrying people and cargo 28 | }; 29 | // --------------------------------------------------------------------------- 30 | 31 | // --------------------------------------------------------------------------- 32 | // "animal.h" 33 | 34 | /* 35 | #include "dog.h" 36 | #include "cat.h" 37 | #include "horse.h" 38 | */ 39 | 40 | using animal_t = cppcon14::variant::variant_t; 41 | // stuff about making sounds and interacting with people 42 | // --------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /calc.test.cc: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | TODO 17 | --------------------------------------------------------------------------- */ 18 | 19 | #include "variant.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "lick.h" 27 | 28 | using namespace std; 29 | using namespace cppcon14::variant; 30 | 31 | /* --------------------------------------------------------------------------- 32 | TODO 33 | --------------------------------------------------------------------------- */ 34 | 35 | /* TODO */ 36 | struct expr_t; 37 | using expr_ptr_t = shared_ptr; 38 | 39 | /* TODO */ 40 | struct val_t; 41 | using val_ptr_t = shared_ptr; 42 | 43 | /* TODO */ 44 | class type_mismatch_error_t final : public runtime_error { 45 | public: 46 | type_mismatch_error_t() : runtime_error("type mismatch error") {} 47 | }; 48 | 49 | /* TODO */ 50 | class null_value_error_t final : public runtime_error { 51 | public: 52 | null_value_error_t() : runtime_error("null value error") {} 53 | }; 54 | 55 | /* TODO */ 56 | class undef_ref_error_t final : public runtime_error { 57 | public: 58 | undef_ref_error_t() : runtime_error("undefined ref error") {} 59 | }; 60 | 61 | /* --------------------------------------------------------------------------- 62 | TODO 63 | --------------------------------------------------------------------------- */ 64 | 65 | /* TODO */ 66 | struct lambda_t final { 67 | using params_t = vector; 68 | lambda_t(params_t &¶ms, const expr_ptr_t &def) 69 | : params(move(params)), def(move(def)) {} 70 | params_t params; 71 | expr_ptr_t def; 72 | }; 73 | 74 | /* TODO */ 75 | struct val_t final : public variant_t { 76 | using variant_t::variant_t; 77 | }; 78 | 79 | /* --------------------------------------------------------------------------- 80 | TODO 81 | --------------------------------------------------------------------------- */ 82 | 83 | /* TODO */ 84 | struct unary_val_functor_t { 85 | using ret_t = val_t; 86 | val_t operator()(null_t) const { throw null_value_error_t(); } 87 | template 88 | val_t operator()(const that_t &) const { 89 | throw type_mismatch_error_t(); 90 | } 91 | }; 92 | 93 | /* TODO */ 94 | struct binary_val_functor_t { 95 | using ret_t = val_t; 96 | val_t operator()(null_t, null_t) const { throw null_value_error_t(); } 97 | template 98 | val_t operator()(const lhs_t &, null_t) const { 99 | throw null_value_error_t(); 100 | } 101 | template 102 | val_t operator()(null_t, const rhs_t &) const { 103 | throw null_value_error_t(); 104 | } 105 | template 106 | val_t operator()(const lhs_t &, const rhs_t &) const { 107 | throw type_mismatch_error_t(); 108 | } 109 | }; 110 | 111 | /* TODO */ 112 | struct neg_t final : unary_val_functor_t { 113 | using unary_val_functor_t::operator(); 114 | val_t operator()(int that) const { return -that; } 115 | }; 116 | 117 | /* TODO */ 118 | struct not_t final : unary_val_functor_t { 119 | using unary_val_functor_t::operator(); 120 | val_t operator()(int that) const { return static_cast(!that); } 121 | }; 122 | 123 | /* TODO */ 124 | struct to_int_t final : unary_val_functor_t { 125 | using unary_val_functor_t::operator(); 126 | val_t operator()(int that) const { return that; } 127 | val_t operator()(const string &that) const { return stoi(that); } 128 | }; 129 | 130 | /* TODO */ 131 | struct to_str_t final : unary_val_functor_t { 132 | using unary_val_functor_t::operator(); 133 | val_t operator()(int that) const { 134 | ostringstream strm; 135 | strm << that; 136 | return strm.str(); 137 | } 138 | val_t operator()(const string &that) const { return that; } 139 | }; 140 | 141 | /* TODO */ 142 | struct add_t final : binary_val_functor_t { 143 | using binary_val_functor_t::operator(); 144 | val_t operator()(int lhs, int rhs) const { return lhs + rhs; } 145 | val_t operator()(const string &lhs, const string &rhs) const { 146 | return lhs + rhs; 147 | } 148 | }; 149 | 150 | /* TODO */ 151 | struct mul_t final : binary_val_functor_t { 152 | using binary_val_functor_t::operator(); 153 | val_t operator()(int lhs, int rhs) const { return lhs * rhs; } 154 | val_t operator()(const string &lhs, int rhs) const { 155 | ostringstream strm; 156 | for (int i = 0; i < rhs; ++i) { 157 | strm << lhs; 158 | } 159 | return strm.str(); 160 | } 161 | }; 162 | 163 | /* TODO */ 164 | struct and_t final : binary_val_functor_t { 165 | using binary_val_functor_t::operator(); 166 | val_t operator()(int lhs, int rhs) const { 167 | return static_cast(lhs && rhs); 168 | } 169 | }; 170 | 171 | /* TODO */ 172 | struct or_t final : binary_val_functor_t { 173 | using binary_val_functor_t::operator(); 174 | val_t operator()(int lhs, int rhs) const { 175 | return static_cast(lhs || rhs); 176 | } 177 | }; 178 | 179 | /* TODO */ 180 | struct lt_t final : binary_val_functor_t { 181 | using binary_val_functor_t::operator(); 182 | val_t operator()(int lhs, int rhs) const { 183 | return static_cast(lhs < rhs); 184 | } 185 | val_t operator()(const string &lhs, const string &rhs) const { 186 | return static_cast(lhs < rhs); 187 | } 188 | }; 189 | 190 | FIXTURE(val_functors) { 191 | EXPECT_EQ(apply(neg_t(), val_t(1)).as(), -1); 192 | EXPECT_EQ(apply(not_t(), val_t(1)).as(), 0); 193 | EXPECT_EQ(apply(to_int_t(), val_t(1)).as(), 1); 194 | EXPECT_EQ(apply(to_int_t(), val_t(string("101"))).as(), 101); 195 | EXPECT_EQ(apply(to_str_t(), val_t(101)).as(), "101"); 196 | EXPECT_EQ(apply(to_str_t(), val_t(string("hello"))).as(), "hello"); 197 | EXPECT_EQ(apply(add_t(), val_t(1), val_t(2)).as(), 3); 198 | EXPECT_EQ(apply(add_t(), val_t(string("hello")), val_t(string("doctor"))).as(), "hellodoctor"); 199 | EXPECT_EQ(apply(mul_t(), val_t(2), val_t(3)).as(), 6); 200 | EXPECT_EQ(apply(mul_t(), val_t(string("two")), val_t(3)).as(), "twotwotwo"); 201 | EXPECT_EQ(apply(lt_t(), val_t(1), val_t(2)).as(), 1); 202 | EXPECT_EQ(apply(and_t(), val_t(1), val_t(1)).as(), 1); 203 | EXPECT_EQ(apply(and_t(), val_t(0), val_t(1)).as(), 0); 204 | EXPECT_EQ(apply(or_t(), val_t(0), val_t(0)).as(), 0); 205 | EXPECT_EQ(apply(or_t(), val_t(0), val_t(1)).as(), 1); 206 | EXPECT_EQ(apply(lt_t(), val_t(string("hello")), val_t(string("doctor"))).as(), 0); 207 | } 208 | 209 | /* --------------------------------------------------------------------------- 210 | TODO 211 | --------------------------------------------------------------------------- */ 212 | 213 | /* TODO */ 214 | class scope_t final { 215 | public: 216 | 217 | /* TODO */ 218 | scope_t(const scope_t &) = delete; 219 | scope_t &operator=(const scope_t &) = delete; 220 | 221 | /* TODO */ 222 | using defs_t = map; 223 | 224 | /* TODO */ 225 | explicit scope_t(const scope_t *parent = nullptr) noexcept : parent(parent) {} 226 | 227 | /* TODO */ 228 | void def(const string &name, val_t &&val) { 229 | assert(this); 230 | defs[name] = move(val); 231 | } 232 | 233 | /* TODO */ 234 | const val_t &ref(const string &name) const { 235 | assert(this); 236 | const scope_t *scope = this; 237 | do { 238 | auto iter = scope->defs.find(name); 239 | if (iter != scope->defs.end()) { 240 | return iter->second; 241 | } 242 | scope = scope->parent; 243 | } while (scope); 244 | throw undef_ref_error_t(); 245 | } 246 | 247 | private: 248 | 249 | /* TODO */ 250 | const scope_t *parent; 251 | 252 | /* TODO */ 253 | defs_t defs; 254 | 255 | }; // scope_t; 256 | 257 | FIXTURE(scope) { 258 | scope_t s1; 259 | s1.def("name", string("alice")); 260 | s1.def("age", 42); 261 | EXPECT_EQ(s1.ref("name").as(), "alice"); 262 | EXPECT_EQ(s1.ref("age").as(), 42); 263 | scope_t s2(&s1); 264 | s2.def("age", 29); 265 | EXPECT_EQ(s2.ref("age").as(), 29); 266 | } 267 | 268 | /* --------------------------------------------------------------------------- 269 | TODO 270 | --------------------------------------------------------------------------- */ 271 | 272 | /* TODO */ 273 | struct lit_t final { 274 | lit_t(const val_ptr_t &val) : val(val) {} 275 | val_ptr_t val; 276 | }; 277 | 278 | /* TODO */ 279 | struct affix_t final { 280 | enum op_t { 281 | neg, 282 | not_, 283 | to_int, 284 | to_str 285 | }; 286 | affix_t(op_t op, const expr_ptr_t &arg) : op(op), arg(arg) {} 287 | op_t op; 288 | expr_ptr_t arg; 289 | }; 290 | 291 | /* TODO */ 292 | struct infix_t final { 293 | enum op_t { 294 | add, 295 | mul, 296 | lt, 297 | and_, 298 | or_ 299 | }; 300 | infix_t(op_t op, const expr_ptr_t &lhs, const expr_ptr_t &rhs) 301 | : op(op), lhs(lhs), rhs(rhs) {} 302 | op_t op; 303 | expr_ptr_t lhs, rhs; 304 | }; 305 | 306 | /* TODO */ 307 | struct ref_t final { 308 | ref_t(string name) : name(move(name)) {} 309 | string name; 310 | }; 311 | 312 | /* TODO */ 313 | struct apply_t final { 314 | using args_t = vector; 315 | apply_t(const expr_ptr_t &fn, args_t args) : fn(fn), args(move(args)) {} 316 | expr_ptr_t fn; 317 | args_t args; 318 | }; 319 | 320 | /* TODO */ 321 | struct expr_t final 322 | : public variant_t { 323 | using variant_t::variant_t; 324 | }; 325 | 326 | /* --------------------------------------------------------------------------- 327 | TODO 328 | --------------------------------------------------------------------------- */ 329 | 330 | /* TODO */ 331 | struct eval_t final { 332 | 333 | /* TODO */ 334 | using ret_t = val_t; 335 | 336 | /* TODO */ 337 | val_t operator()(nullptr_t) const { return val_t(); } 338 | 339 | /* TODO */ 340 | val_t operator()(const lit_t &that) const { 341 | return *that.val; 342 | } 343 | 344 | /* TODO */ 345 | val_t operator()(const affix_t &that) const { 346 | val_t result, arg = apply(*this, *that.arg); 347 | switch (that.op) { 348 | case affix_t::neg: { 349 | result = apply(neg_t(), arg); 350 | break; 351 | } 352 | case affix_t::not_: { 353 | result = apply(not_t(), arg); 354 | break; 355 | } 356 | case affix_t::to_int: { 357 | result = apply(to_int_t(), arg); 358 | break; 359 | } 360 | case affix_t::to_str: { 361 | result = apply(to_str_t(), arg); 362 | break; 363 | } 364 | } // switch 365 | return move(result); 366 | } 367 | 368 | /* TODO */ 369 | val_t operator()(const infix_t &that) const { 370 | val_t result, lhs = apply(*this, *that.lhs), rhs = apply(*this, *that.rhs); 371 | switch (that.op) { 372 | case infix_t::add: { 373 | result = apply(add_t(), lhs, rhs); 374 | break; 375 | } 376 | case infix_t::mul: { 377 | result = apply(mul_t(), lhs, rhs); 378 | break; 379 | } 380 | case infix_t::lt: { 381 | result = apply(lt_t(), lhs, rhs); 382 | break; 383 | } 384 | case infix_t::and_: { 385 | result = apply(and_t(), lhs, rhs); 386 | break; 387 | } 388 | case infix_t::or_: { 389 | result = apply(or_t(), lhs, rhs); 390 | break; 391 | } 392 | } // switch 393 | return move(result); 394 | } 395 | 396 | /* TODO */ 397 | val_t operator()(const ref_t &that) const { 398 | return scope->ref(that.name); 399 | } 400 | 401 | /* TODO */ 402 | val_t operator()(const apply_t &that) const { 403 | /* Get the lambda we're going to apply. */ 404 | val_t fn = apply(*this, *that.fn); 405 | const auto *lambda = fn.try_as(); 406 | if (!lambda || lambda->params.size() != that.args.size()) { 407 | throw type_mismatch_error_t(); 408 | } 409 | /* Evaluate the arguments and put them into scope. */ 410 | scope_t local_scope(scope); 411 | for (size_t i = 0; i < that.args.size(); ++i) { 412 | local_scope.def(lambda->params[i], apply(*this, *that.args[i])); 413 | } 414 | /* Evaluate the lambda's definition. */ 415 | return apply(eval_t{&local_scope}, *lambda->def); 416 | } 417 | 418 | const scope_t *scope; 419 | 420 | }; 421 | 422 | /* --------------------------------------------------------------------------- 423 | TODO 424 | --------------------------------------------------------------------------- */ 425 | 426 | /* TODO */ 427 | struct token_t final { 428 | enum kind_t { 429 | end, 430 | open_paren, 431 | close_paren, 432 | plus, 433 | minus, 434 | star, 435 | lt, 436 | eq, 437 | comma, 438 | lit, 439 | name, 440 | and_kwd, 441 | fn_kwd, 442 | int_kwd, 443 | not_kwd, 444 | or_kwd, 445 | str_kwd 446 | }; 447 | kind_t kind; 448 | val_ptr_t val; 449 | }; 450 | 451 | /* --------------------------------------------------------------------------- 452 | TODO 453 | --------------------------------------------------------------------------- */ 454 | 455 | /* TODO */ 456 | class scanner_t final { 457 | public: 458 | 459 | /* No copying or moving. */ 460 | scanner_t(const scanner_t &) = delete; 461 | scanner_t &operator=(const scanner_t &) = delete; 462 | 463 | /* TODO */ 464 | scanner_t(istream &strm) 465 | : strm(strm), token_is_cached(false) { 466 | assert(strm); 467 | } 468 | 469 | /* TODO */ 470 | const token_t &operator*() const { 471 | assert(this); 472 | refresh_cache(); 473 | return cached_token; 474 | } 475 | 476 | /* TODO */ 477 | const token_t *operator->() const { 478 | assert(this); 479 | refresh_cache(); 480 | return &cached_token; 481 | } 482 | 483 | /* TODO */ 484 | scanner_t &operator++() { 485 | assert(this); 486 | refresh_cache(); 487 | if (token_is_cached && cached_token.kind != token_t::end) { 488 | cached_token.val.reset(); 489 | token_is_cached = false; 490 | } 491 | return *this; 492 | } 493 | 494 | private: 495 | 496 | /* TODO */ 497 | void refresh_cache() const { 498 | assert(this); 499 | if (!token_is_cached) { 500 | enum { start, str_lit, int_lit, name } state = start; 501 | ostringstream accum; 502 | do { 503 | auto c = strm.peek(); 504 | switch (state) { 505 | case start: { 506 | switch (c) { 507 | case '(': { 508 | strm.ignore(); 509 | cached_token.kind = token_t::open_paren; 510 | token_is_cached = true; 511 | break; 512 | } 513 | case ')': { 514 | strm.ignore(); 515 | cached_token.kind = token_t::close_paren; 516 | token_is_cached = true; 517 | break; 518 | } 519 | case '+': { 520 | strm.ignore(); 521 | cached_token.kind = token_t::plus; 522 | token_is_cached = true; 523 | break; 524 | } 525 | case '-': { 526 | strm.ignore(); 527 | cached_token.kind = token_t::minus; 528 | token_is_cached = true; 529 | break; 530 | } 531 | case '*': { 532 | strm.ignore(); 533 | cached_token.kind = token_t::star; 534 | token_is_cached = true; 535 | break; 536 | } 537 | case '<': { 538 | strm.ignore(); 539 | cached_token.kind = token_t::lt; 540 | token_is_cached = true; 541 | break; 542 | } 543 | case '=': { 544 | strm.ignore(); 545 | cached_token.kind = token_t::eq; 546 | token_is_cached = true; 547 | break; 548 | } 549 | case ',': { 550 | strm.ignore(); 551 | cached_token.kind = token_t::comma; 552 | token_is_cached = true; 553 | break; 554 | } 555 | case '"': { 556 | strm.ignore(); 557 | state = str_lit; 558 | break; 559 | } 560 | default: { 561 | if (c < 0) { 562 | cached_token.kind = token_t::end; 563 | token_is_cached = true; 564 | break; 565 | } 566 | if (isspace(c)) { 567 | strm.ignore(); 568 | break; 569 | } 570 | if (isdigit(c)) { 571 | state = int_lit; 572 | break; 573 | } 574 | if (isalpha(c) || c == '_') { 575 | state = name; 576 | break; 577 | } 578 | throw; 579 | } 580 | } // switch 581 | break; 582 | } 583 | case str_lit: { 584 | if (c < 0) { 585 | throw; 586 | } 587 | if (c == '"') { 588 | strm.ignore(); 589 | cached_token.kind = token_t::lit; 590 | cached_token.val = make_shared(accum.str()); 591 | token_is_cached = true; 592 | break; 593 | } 594 | if (isprint(c)) { 595 | strm.ignore(); 596 | accum.put(c); 597 | break; 598 | } 599 | throw; 600 | } 601 | case int_lit: { 602 | if (isdigit(c)) { 603 | strm.ignore(); 604 | accum.put(c); 605 | break; 606 | } 607 | istringstream strm(accum.str()); 608 | int temp; 609 | strm >> temp; 610 | cached_token.kind = token_t::lit; 611 | cached_token.val = make_shared(temp); 612 | token_is_cached = true; 613 | break; 614 | } 615 | case name: { 616 | if (isalnum(c) || c == '_') { 617 | strm.ignore(); 618 | accum.put(c); 619 | break; 620 | } 621 | string name = accum.str(); 622 | static const map kwds = { 623 | { "and", token_t::and_kwd }, 624 | { "fn", token_t::fn_kwd }, 625 | { "int", token_t::int_kwd }, 626 | { "not", token_t::not_kwd }, 627 | { "or", token_t::or_kwd }, 628 | { "str", token_t::str_kwd } 629 | }; 630 | auto iter = kwds.find(name); 631 | if (iter != kwds.end()) { 632 | cached_token.kind = iter->second; 633 | } else { 634 | cached_token.kind = token_t::name; 635 | cached_token.val = make_shared(accum.str()); 636 | } 637 | token_is_cached = true; 638 | break; 639 | } 640 | } // switch 641 | } while (!token_is_cached); 642 | } // if 643 | } 644 | 645 | /* TODO */ 646 | istream &strm; 647 | 648 | /* TODO */ 649 | mutable bool token_is_cached; 650 | 651 | /* TODO */ 652 | mutable token_t cached_token; 653 | 654 | }; // scanner_t 655 | 656 | FIXTURE(scanner) { 657 | istringstream strm("1 + 2"); 658 | scanner_t scanner(strm); 659 | if (EXPECT_TRUE(scanner->kind == token_t::lit)) { 660 | EXPECT_EQ(scanner->val->as(), 1); 661 | } 662 | ++scanner; 663 | EXPECT_TRUE(scanner->kind == token_t::plus); 664 | ++scanner; 665 | if (EXPECT_TRUE(scanner->kind == token_t::lit)) { 666 | EXPECT_EQ(scanner->val->as(), 2); 667 | } 668 | ++scanner; 669 | EXPECT_TRUE(scanner->kind == token_t::end); 670 | } 671 | 672 | /* --------------------------------------------------------------------------- 673 | TODO 674 | --------------------------------------------------------------------------- */ 675 | 676 | /* TODO */ 677 | class expr_parser_t final { 678 | public: 679 | 680 | /* TODO */ 681 | static expr_ptr_t parse(scanner_t &scanner) { 682 | return expr_parser_t(scanner).parse_expr(); 683 | } 684 | 685 | private: 686 | 687 | /* TODO */ 688 | expr_parser_t(scanner_t &scanner) 689 | : scanner(scanner) {} 690 | 691 | /* TODO */ 692 | expr_ptr_t parse_and() { 693 | assert(this); 694 | expr_ptr_t result = parse_not(); 695 | while (scanner->kind == token_t::and_kwd) { 696 | ++scanner; 697 | result = make_shared( 698 | infix_t(infix_t::or_, result, parse_not())); 699 | } 700 | return result; 701 | } 702 | 703 | /* TODO */ 704 | expr_ptr_t parse_arith() { 705 | assert(this); 706 | expr_ptr_t result = parse_term(); 707 | while (scanner->kind == token_t::plus) { 708 | ++scanner; 709 | result = make_shared( 710 | infix_t(infix_t::add, result, parse_term())); 711 | } 712 | return result; 713 | } 714 | 715 | /* TODO */ 716 | expr_ptr_t parse_atom() { 717 | assert(this); 718 | expr_ptr_t result; 719 | switch (scanner->kind) { 720 | case token_t::lit: { 721 | result = make_shared(lit_t(scanner->val)); 722 | ++scanner; 723 | break; 724 | } 725 | case token_t::name: { 726 | result = make_shared(ref_t(scanner->val->as())); 727 | ++scanner; 728 | break; 729 | } 730 | case token_t::open_paren: { 731 | ++scanner; 732 | result = parse_expr(); 733 | match(token_t::close_paren); 734 | break; 735 | } 736 | default: { 737 | throw; 738 | } 739 | } // switch 740 | return result; 741 | } 742 | 743 | /* TODO */ 744 | expr_ptr_t parse_cmp() { 745 | assert(this); 746 | expr_ptr_t result = parse_arith(); 747 | if (scanner->kind == token_t::lt) { 748 | ++scanner; 749 | result = make_shared( 750 | infix_t(infix_t::lt, result, parse_arith())); 751 | } 752 | return result; 753 | } 754 | 755 | /* TODO */ 756 | expr_ptr_t parse_expr() { 757 | assert(this); 758 | expr_ptr_t result; 759 | switch (scanner->kind) { 760 | case token_t::fn_kwd: { 761 | ++scanner; 762 | lambda_t::params_t params; 763 | for (;;) { 764 | if (scanner->kind == token_t::eq) { 765 | ++scanner; 766 | break; 767 | } 768 | if (scanner->kind != token_t::name) { 769 | throw; 770 | } 771 | params.emplace_back(scanner->val->as()); 772 | ++scanner; 773 | match(token_t::comma); 774 | } 775 | result = make_shared( 776 | lit_t(make_shared( 777 | lambda_t(move(params), parse_expr())))); 778 | break; 779 | } 780 | default: { 781 | result = parse_or(); 782 | } 783 | } 784 | return result; 785 | } 786 | 787 | /* TODO */ 788 | expr_ptr_t parse_factor() { 789 | assert(this); 790 | bool flag = false; 791 | for (;;) { 792 | if (scanner->kind == token_t::plus) { 793 | ++scanner; 794 | continue; 795 | } 796 | if (scanner->kind == token_t::minus) { 797 | ++scanner; 798 | flag = !flag; 799 | continue; 800 | } 801 | break; 802 | } 803 | expr_ptr_t result = parse_atom(); 804 | if (flag) { 805 | result = make_shared(affix_t(affix_t::neg, result)); 806 | } 807 | return result; 808 | } 809 | 810 | /* TODO */ 811 | expr_ptr_t parse_not() { 812 | assert(this); 813 | bool flag = false; 814 | while (scanner->kind == token_t::not_kwd) { 815 | ++scanner; 816 | flag = !flag; 817 | } 818 | expr_ptr_t result = parse_cmp(); 819 | if (flag) { 820 | result = make_shared(affix_t(affix_t::not_, result)); 821 | } 822 | return result; 823 | } 824 | 825 | /* TODO */ 826 | expr_ptr_t parse_or() { 827 | assert(this); 828 | expr_ptr_t result = parse_and(); 829 | while (scanner->kind == token_t::or_kwd) { 830 | ++scanner; 831 | result = make_shared( 832 | infix_t(infix_t::or_, result, parse_and())); 833 | } 834 | return result; 835 | } 836 | 837 | /* TODO */ 838 | expr_ptr_t parse_term() { 839 | assert(this); 840 | expr_ptr_t result = parse_factor(); 841 | while (scanner->kind == token_t::star) { 842 | ++scanner; 843 | result = make_shared( 844 | infix_t(infix_t::mul, result, parse_factor())); 845 | } 846 | return result; 847 | } 848 | 849 | private: 850 | 851 | /* TODO */ 852 | void match(token_t::kind_t kind) { 853 | assert(this); 854 | if (scanner->kind != kind) { 855 | throw; 856 | } 857 | ++scanner; 858 | } 859 | 860 | /* TODO */ 861 | scanner_t &scanner; 862 | 863 | }; // expr_parser_t 864 | 865 | /* TODO */ 866 | inline expr_ptr_t parse_expr(const string &text) { 867 | istringstream strm(text); 868 | scanner_t scanner(strm); 869 | return expr_parser_t::parse(scanner); 870 | } 871 | 872 | /* TODO */ 873 | inline val_t eval(const string &text) { 874 | scope_t scope; 875 | return apply(eval_t{&scope}, *parse_expr(text)); 876 | } 877 | 878 | /* TODO */ 879 | inline string eval_as_str(const string &text) { 880 | return apply(to_str_t(), eval(text)).as(); 881 | } 882 | 883 | FIXTURE(parse_expr) { 884 | EXPECT_EQ(eval_as_str("1 + 2"), "3"); 885 | } 886 | 887 | -------------------------------------------------------------------------------- /intersects.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shapes4.h" 4 | 5 | bool intersects(const shape_t &lhs, const shape_t &rhs) { 6 | return cppcon14::variant::match( 7 | lhs, rhs, 8 | [](const circle_t &, const circle_t &) { /* ... */ }, 9 | [](const circle_t &, const square_t &) { /* ... */ }, 10 | [](const circle_t &, const triangle_t &) { /* ... */ }, 11 | [](const square_t &, const circle_t &) { /* ... */ }, 12 | [](const square_t &, const square_t &) { /* ... */ }, 13 | [](const square_t &, const triangle_t &) { /* ... */ }, 14 | [](const triangle_t &, const circle_t &) { /* ... */ }, 15 | [](const triangle_t &, const square_t &) { /* ... */ }, 16 | [](const triangle_t &, const triangle_t &) { /* ... */ }); 17 | } 18 | -------------------------------------------------------------------------------- /intersects.test.cc: -------------------------------------------------------------------------------- 1 | #include "intersects.h" 2 | #include "variant.h" 3 | 4 | #include "lick.h" 5 | 6 | FIXTURE(intersects) { 7 | shape_t lhs = circle_t{{0, 0}, 101}; 8 | shape_t rhs = square_t{{0, 0}, 202}; 9 | EXPECT_TRUE(intersects(lhs, rhs)); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lick.cc: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | This module simplifies building unit tests with lick. It contains nothing 17 | but a main function, and this main just hands off the the main entry point 18 | in lick, parsing the command line and initiating the test fixture driver. 19 | 20 | Compile and link your unit test modules with this module, or provide your 21 | own main, whichever you prefer. 22 | --------------------------------------------------------------------------- */ 23 | 24 | #include "lick.h" 25 | 26 | int main(int argc, char *argv[]) { 27 | return cppcon14::lick::main(argc, argv); 28 | } 29 | -------------------------------------------------------------------------------- /lick.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | TODO 17 | --------------------------------------------------------------------------- */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | /* A macro to define a fixture. */ 36 | #ifndef FIXTURE 37 | #define FIXTURE(name) \ 38 | static void name##_func(); \ 39 | static const ::cppcon14::lick::fixture_t \ 40 | name##_fixture(__LINE__, #name, name##_func); \ 41 | static void name##_func() 42 | #endif 43 | 44 | /* Macros to construct a expectations. */ 45 | #ifndef EXPECT_EQ 46 | #define EXPECT_EQ(lhs, rhs) ::cppcon14::lick::expect_t( \ 47 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::eq, #rhs, rhs) 48 | #endif 49 | #ifndef EXPECT_NE 50 | #define EXPECT_NE(lhs, rhs) ::cppcon14::lick::expect_t( \ 51 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::ne, #rhs, rhs) 52 | #endif 53 | #ifndef EXPECT_LT 54 | #define EXPECT_LT(lhs, rhs) ::cppcon14::lick::expect_t( \ 55 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::lt, #rhs, rhs) 56 | #endif 57 | #ifndef EXPECT_LE 58 | #define EXPECT_LE(lhs, rhs) ::cppcon14::lick::expect_t( \ 59 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::le, #rhs, rhs) 60 | #endif 61 | #ifndef EXPECT_GT 62 | #define EXPECT_GT(lhs, rhs) ::cppcon14::lick::expect_t( \ 63 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::gt, #rhs, rhs) 64 | #endif 65 | #ifndef EXPECT_GE 66 | #define EXPECT_GE(lhs, rhs) ::cppcon14::lick::expect_t( \ 67 | __LINE__, #lhs, lhs, ::cppcon14::lick::expect_t::ge, #rhs, rhs) 68 | #endif 69 | #ifndef EXPECT_TRUE 70 | #define EXPECT_TRUE(arg) ::cppcon14::lick::expect_t( \ 71 | __LINE__, ::cppcon14::lick::expect_t::pt, #arg, arg) 72 | #endif 73 | #ifndef EXPECT_FALSE 74 | #define EXPECT_FALSE(arg) ::cppcon14::lick::expect_t( \ 75 | __LINE__, ::cppcon14::lick::expect_t::pf, #arg, arg) 76 | #endif 77 | 78 | namespace cppcon14 { 79 | namespace lick { 80 | 81 | /* Manages the single instance of a class (obj_t) with singleton semantics. 82 | We don't create or destroy this instance, but we do keep track of it 83 | while it exists, preventing more than one instance from existing at a 84 | time. */ 85 | template 86 | class singleton_t final { 87 | public: 88 | 89 | /* No construction, copying, or moving. */ 90 | singleton_t() = delete; 91 | singleton_t(const singleton_t &) = delete; 92 | singleton_t &operator=(const singleton_t &) = delete; 93 | 94 | /* The current instance of obj_t. Never null. */ 95 | static obj_t *get_instance() noexcept { 96 | assert(instance); 97 | return instance; 98 | } 99 | 100 | /* Called by the constructor of obj_t to register the creation of the 101 | singleton instance. */ 102 | static void on_construction(obj_t *obj) { 103 | assert(obj); 104 | assert(!instance); 105 | instance = obj; 106 | } 107 | 108 | /* Called by the destructor of obj_t to register the destruction of the 109 | singleton instance. */ 110 | static void on_destruction(obj_t *obj) { 111 | assert(obj); 112 | assert(instance == obj); 113 | instance = nullptr; 114 | } 115 | 116 | private: 117 | 118 | /* The single instance of obj_t, or null if there is none right now. */ 119 | static obj_t *instance; 120 | 121 | }; // singleton_t 122 | 123 | /* See declaration. */ 124 | template 125 | obj_t *singleton_t::instance = nullptr; 126 | 127 | /* A node in a linked list of instances of obj_t in the data segment. We 128 | don't create or destroy the instances of obj_t, we just maintain a 129 | linked list of them. The intent is for an instance of this class to be 130 | aggregated within obj_t and to live along with as constant data it in the 131 | data segment. */ 132 | template 133 | class static_list_node_t final { 134 | public: 135 | 136 | /* No copying or moving. */ 137 | static_list_node_t(const static_list_node_t &) = delete; 138 | static_list_node_t &operator=(const static_list_node_t &) = delete; 139 | 140 | /* Caches the pointer to the object and links this node to the end of 141 | the list. */ 142 | explicit static_list_node_t(const obj_t *obj) 143 | : obj(obj), next_node(nullptr) { 144 | assert(obj); 145 | (last_node ? last_node->next_node : first_node) = this; 146 | last_node = this; 147 | } 148 | 149 | /* This is an explicit do-nothing. As we assume the instances of obj_t 150 | and static_list_node_t are in the data segment, we don't have to clean 151 | them up. */ 152 | ~static_list_node_t() {} 153 | 154 | /* Call back for each instance of obj_t. */ 155 | static bool for_each_instance( 156 | const std::function &cb) { 157 | bool ok = true; 158 | for (auto *node = first_node; node && ok; node = node->next_node) { 159 | ok = cb(node->obj); 160 | } 161 | return ok; 162 | } 163 | 164 | private: 165 | 166 | /* The object kept at this point in the linked list. Never null. */ 167 | const obj_t *obj; 168 | 169 | /* The next node in the linked list. */ 170 | static_list_node_t *next_node; 171 | 172 | /* The first and last nodes in the linked list. */ 173 | static static_list_node_t *first_node, *last_node; 174 | 175 | }; // static_list_node_t 176 | 177 | /* See declarations. */ 178 | template 179 | static_list_node_t *static_list_node_t::first_node = nullptr; 180 | template 181 | static_list_node_t *static_list_node_t::last_node = nullptr; 182 | 183 | /* TODO */ 184 | template 185 | class opt_t final { 186 | public: 187 | 188 | /* TODO */ 189 | opt_t() noexcept 190 | : known(false) {} 191 | 192 | /* TODO */ 193 | opt_t(elem_t that) noexcept { 194 | new (&elem) elem_t(std::move(that)); 195 | known = true; 196 | } 197 | 198 | /* TODO */ 199 | opt_t(opt_t &&that) noexcept { 200 | assert(&that); 201 | if (that.known) { 202 | new (&elem) elem_t(std::move(that.elem)); 203 | that.elem.~elem_t(); 204 | } 205 | known = that.known; 206 | that.known = false; 207 | } 208 | 209 | /* TODO */ 210 | opt_t(const opt_t &that) { 211 | assert(&that); 212 | if (that.known) { 213 | new (&elem) elem_t(that.elem); 214 | } 215 | known = that.known; 216 | } 217 | 218 | /* TODO */ 219 | ~opt_t() { 220 | assert(this); 221 | if (known) { 222 | elem.~elem_t(); 223 | } 224 | } 225 | 226 | /* TODO */ 227 | operator bool () const noexcept { 228 | assert(this); 229 | return known; 230 | } 231 | 232 | /* TODO */ 233 | opt_t &operator=(opt_t &&that) noexcept { 234 | assert(this); 235 | assert(&that); 236 | this->~opt_t(); 237 | new (this) opt_t(std::move(that)); 238 | return *this; 239 | } 240 | 241 | /* TODO */ 242 | opt_t &operator=(const opt_t &that) noexcept { 243 | assert(this); 244 | assert(&that); 245 | return *this = opt_t(that); 246 | } 247 | 248 | /* TODO */ 249 | const elem_t &get(const elem_t &def_elem) const noexcept { 250 | assert(this); 251 | assert(&def_elem); 252 | return known ? elem : def_elem; 253 | } 254 | 255 | /* TODO */ 256 | bool try_set(elem_t that) { 257 | assert(this); 258 | bool result; 259 | if (!known) { 260 | new (&elem) elem_t(std::move(that)); 261 | result = true; 262 | } else { 263 | result = false; 264 | } 265 | return result; 266 | } 267 | 268 | private: 269 | 270 | /* TODO */ 271 | bool known; 272 | 273 | /* TODO */ 274 | union { 275 | elem_t elem; 276 | }; 277 | 278 | }; // opt_t 279 | 280 | /* TODO */ 281 | struct config_t final { 282 | 283 | /* TODO */ 284 | enum output_t { to_stdout, to_stderr, to_nowhere }; 285 | 286 | /* TODO */ 287 | enum style_t { terse, verbose }; 288 | 289 | /* TODO */ 290 | struct channel_t final { 291 | 292 | /* TODO */ 293 | std::ostream *get_strm() const noexcept { 294 | assert(this); 295 | return config_t::get_strm(output); 296 | } 297 | 298 | /* TODO */ 299 | output_t output; 300 | 301 | /* TODO */ 302 | style_t style; 303 | 304 | }; // config_t::channel_t 305 | 306 | /* A reasonable default configuration. */ 307 | config_t() noexcept 308 | : prog_name(nullptr), 309 | pass_channel({ to_stdout, terse }), 310 | fail_channel({ to_stdout, verbose }), 311 | summary_output(to_stdout), use_color(true) {} 312 | 313 | /* Terminal codes for red text. */ 314 | const char *red() const noexcept { 315 | assert(this); 316 | return use_color ? "\033[1;31m" : ""; 317 | } 318 | 319 | /* Terminal codes for green text. */ 320 | const char *green() const noexcept { 321 | assert(this); 322 | return use_color ? "\033[1;32m" : ""; 323 | } 324 | 325 | /* Terminal codes for plain (that is, not red or green) text. */ 326 | const char *plain() const noexcept { 327 | assert(this); 328 | return use_color ? "\033[0m" : ""; 329 | } 330 | 331 | /* TODO */ 332 | std::ostream *get_summary_strm() const noexcept { 333 | assert(this); 334 | return get_strm(summary_output); 335 | } 336 | 337 | /* TODO */ 338 | static std::ostream *get_strm(output_t output) noexcept { 339 | std::ostream *result; 340 | switch (output) { 341 | case to_stdout: { 342 | result = &std::cout; 343 | break; 344 | } 345 | case to_stderr: { 346 | result = &std::cerr; 347 | break; 348 | } 349 | case to_nowhere: { 350 | result = nullptr; 351 | break; 352 | } 353 | } 354 | return result; 355 | } 356 | 357 | /* TODO */ 358 | const char *prog_name; 359 | 360 | /* TODO */ 361 | channel_t pass_channel, fail_channel; 362 | 363 | /* TODO */ 364 | output_t summary_output; 365 | 366 | /* TODO */ 367 | bool use_color; 368 | 369 | /* TODO */ 370 | std::unique_ptr fixture_name_regex; 371 | 372 | }; // config_t 373 | 374 | /* A single expectation within a fixture, like "EXPECT_EQ(a, b)". The 375 | expectation object should be constructed as a temporary and only while a 376 | an instance of outcome collector exists. An expectation object will 377 | implicitly cast to bool, whether the expectation was met. While the 378 | temporary expectation object exists, it can also be the target of 379 | insertion operators, allowing you to write additional descriptive text 380 | into the expectation. */ 381 | class expect_t final { 382 | public: 383 | 384 | /* No copying or moving. */ 385 | expect_t(const expect_t &) = delete; 386 | expect_t &operator=(const expect_t &) = delete; 387 | 388 | /* The actual outcome of an expectation. This class keeps most of its 389 | data in public fields, as it mostly just records stuff as it 390 | happens. */ 391 | class outcome_t final { 392 | public: 393 | 394 | /* Shallow-copying is ok. */ 395 | outcome_t(const outcome_t &) = default; 396 | outcome_t &operator=(const outcome_t &) = default; 397 | 398 | /* Collects outcomes. This class enforces singleton semantics. Make 399 | sure there is an instance of this class before you try constructing 400 | any expectations. */ 401 | class collector_t { 402 | public: 403 | 404 | /* No copying or moving. */ 405 | collector_t(const collector_t &) = delete; 406 | collector_t &operator=(const collector_t &) = delete; 407 | 408 | /* Convenience. */ 409 | using singleton_t = lick::singleton_t; 410 | 411 | /* TODO */ 412 | collector_t() 413 | : ok(true) { 414 | singleton_t::on_construction(this); 415 | } 416 | 417 | /* TODO */ 418 | ~collector_t() { 419 | singleton_t::on_destruction(this); 420 | } 421 | 422 | /* Collect the given outcome and updating our overall success 423 | accordingly. */ 424 | void on_outcome(std::unique_ptr &&outcome) { 425 | assert(this); 426 | assert(outcome); 427 | ok = ok && outcome->ok; 428 | outcomes.emplace_back(std::move(outcome)); 429 | } 430 | 431 | /* TODO */ 432 | void on_exception() { 433 | assert(this); 434 | ok = false; 435 | } 436 | 437 | /* TODO */ 438 | void on_exception(const std::exception &) { 439 | assert(this); 440 | ok = false; 441 | } 442 | 443 | /* True iff. no expectation has failed. */ 444 | operator bool() const noexcept { 445 | assert(this); 446 | return ok; 447 | } 448 | 449 | /* TODO */ 450 | void report(std::ostream &strm, const config_t &config) const { 451 | assert(this); 452 | for (const auto &outcome: outcomes) { 453 | outcome->report(strm, config); 454 | } 455 | } 456 | 457 | /* TODO */ 458 | static collector_t *get_instance() noexcept { 459 | return singleton_t::get_instance(); 460 | } 461 | 462 | private: 463 | 464 | /* TODO */ 465 | bool ok; 466 | 467 | /* TODO */ 468 | std::vector> outcomes; 469 | 470 | }; // expect_t::outcome_t::collector_t 471 | 472 | /* Cache the source line number, as it can't change, and assume the 473 | expectation will not be met. */ 474 | explicit outcome_t(int line) noexcept 475 | : line(line), ok(false) { 476 | assert(line > 0); 477 | } 478 | 479 | /* Write a human-readable report. */ 480 | void report(std::ostream &strm, const config_t &config) const { 481 | assert(this); 482 | assert(&strm); 483 | assert(&config); 484 | if (ok) { 485 | strm << config.green() << " [ " << std::setw(4) << line 486 | << ", pass: " << src << " ]" << config.plain() << std::endl; 487 | } else { 488 | strm << config.red() << " [ " << std::setw(4) << line 489 | << ", fail: " << src << " ]" << config.plain() << std::endl 490 | << " expr: " << expr << std::endl; 491 | if (!desc.empty()) { 492 | strm << " desc: " << desc << std::endl; 493 | } 494 | } 495 | } 496 | 497 | /* The 1-based line number within the fixture at which the expectation 498 | was constructed. This is constant because it really won't ever 499 | change. */ 500 | const int line; 501 | 502 | /* True iff. the expectation was met. */ 503 | bool ok; 504 | 505 | /* The source text of the expectation, like "EXPECT_EQ(a, b)". */ 506 | std::string src; 507 | 508 | /* The expression evaluated to determine if the expectation was met, 509 | like "101 == 202". */ 510 | std::string expr; 511 | 512 | /* Additional descriptive text inserted into the (temporary) expectation 513 | object while it exists. */ 514 | std::string desc; 515 | 516 | }; // expect_t::outcome_t 517 | 518 | /* The infix and prefix operators used in our constructors. */ 519 | enum infix_op_t { eq, ne, lt, le, gt, ge }; 520 | enum prefix_op_t { pt, pf }; 521 | 522 | /* Construct an infix-style text, "like EXPECT_EQ(a, b)". We record the 523 | line number at which the expectation is constructed, the source code 524 | and expressions used on its left- and right-hand sides, and the infix 525 | operator giving the expected condition. */ 526 | template 527 | expect_t( 528 | int line, 529 | const char *lhs_src, const lhs_t &lhs_expr, 530 | infix_op_t op, 531 | const char *rhs_src, const rhs_t &rhs_expr) 532 | : expect_t(line) { 533 | /* Interpret the operator. */ 534 | const char *src, *op_expr; 535 | switch (op) { 536 | case eq: 537 | outcome->ok = (lhs_expr == rhs_expr); 538 | src = "EQ"; op_expr = " == "; 539 | break; 540 | case ne: 541 | outcome->ok = (lhs_expr != rhs_expr); 542 | src = "NE"; op_expr = " != "; 543 | break; 544 | case lt: 545 | outcome->ok = (lhs_expr < rhs_expr); 546 | src = "LT"; op_expr = " < "; 547 | break; 548 | case le: 549 | outcome->ok = (lhs_expr <= rhs_expr); 550 | src = "LE"; op_expr = " <= "; 551 | break; 552 | case gt: 553 | outcome->ok = (lhs_expr > rhs_expr); 554 | src = "GT"; op_expr = " > "; 555 | break; 556 | case ge: 557 | outcome->ok = (lhs_expr >= rhs_expr); 558 | src = "GE"; op_expr = " >= "; 559 | break; 560 | } 561 | /* Build the outcome strings. */ 562 | strm << "EXPECT_" << src << '(' << lhs_src << ", " << rhs_src << ')'; 563 | outcome->src = take_str(); 564 | strm << lhs_expr << op_expr << rhs_expr; 565 | outcome->expr = take_str(); 566 | } 567 | 568 | /* Construct a prefix-style text, "like EXPECT_TRUE(a)". We record the 569 | line number at which the expectation is constructed, the prefix 570 | operator giving the expected condition, and the source code and 571 | expression used as the argument. */ 572 | template 573 | expect_t( 574 | int line, 575 | prefix_op_t op, const char *arg_text, const arg_t &arg_expr) 576 | : expect_t(line) { 577 | /* Interpret the operator. */ 578 | const char *src, *op_expr; 579 | switch (op) { 580 | case pt: 581 | outcome->ok = static_cast(arg_expr); 582 | src = "TRUE"; op_expr = ""; 583 | break; 584 | case pf: 585 | outcome->ok = !static_cast(arg_expr); 586 | src = "FALSE"; op_expr = "!"; 587 | break; 588 | } 589 | /* Build the outcome strings. */ 590 | strm << "EXPECT_" << src << '(' << arg_text << ')'; 591 | outcome->src = take_str(); 592 | strm << op_expr << arg_expr; 593 | outcome->expr = take_str(); 594 | } 595 | 596 | /* As we go, we finalize our outcome and hand it to the current outcome 597 | collector. */ 598 | ~expect_t() { 599 | assert(this); 600 | outcome->desc = take_str(); 601 | outcome_t::collector_t::get_instance()->on_outcome(std::move(outcome)); 602 | } 603 | 604 | /* True iff. the expectation passed. */ 605 | operator bool() const noexcept { 606 | assert(this); 607 | return outcome->ok; 608 | } 609 | 610 | /* We use this as a string-builder for the (optional) description of the 611 | expectation. See the operator<< overload declared after the close 612 | of this namespace. */ 613 | std::ostream &get_strm() noexcept { 614 | assert(this); 615 | return strm; 616 | } 617 | 618 | private: 619 | 620 | /* The other constructors delegate their common work to this one. */ 621 | explicit expect_t(int line) 622 | : outcome(new outcome_t(line)) {} 623 | 624 | /* Take a copy of the string currently built in strm, then reset strm to 625 | its starting state so we can use it to build another string. */ 626 | std::string take_str() { 627 | assert(this); 628 | auto temp = strm.str(); 629 | strm.str(std::string()); 630 | strm.clear(); 631 | return std::move(temp); 632 | } 633 | 634 | /* The outcome of this expectation. We construct this during our own 635 | constructor, then finalize it and move it to the collector during our 636 | destructor. */ 637 | std::unique_ptr outcome; 638 | 639 | /* See accessor. We use this as a string-builder during construction, 640 | for the outcome src and expr strings. After construction, we use 641 | it to collect the outcome desc string. */ 642 | std::ostringstream strm; 643 | 644 | }; // expect_t 645 | 646 | /* TODO */ 647 | using collector_t = expect_t::outcome_t::collector_t; 648 | 649 | /* Meta-data providing the name and location of a function which will 650 | conduct QA experiments. We expect this class to be instantiated only as 651 | constants in the data segment. */ 652 | class fixture_t final { 653 | public: 654 | 655 | /* No copying or moving. */ 656 | fixture_t(const fixture_t &) = delete; 657 | fixture_t &operator=(const fixture_t &) = delete; 658 | 659 | /* The signature of a text fixture function. */ 660 | using func_t = void (*)(); 661 | 662 | /* This helper maintains a list of instances of fixtures. */ 663 | using static_list_node_t = lick::static_list_node_t; 664 | 665 | /* Link to the end of the list of instances and cache the construction 666 | parameters. */ 667 | fixture_t(int line, const char *name, func_t func) 668 | : node(this), line(line), name(name), func(func) { 669 | assert(line > 0); 670 | assert(name); 671 | assert(*name); 672 | assert(func); 673 | } 674 | 675 | /* Run the fixture. */ 676 | void operator()() const noexcept { 677 | assert(this); 678 | try { 679 | func(); 680 | } catch (const std::exception &ex) { 681 | collector_t::get_instance()->on_exception(ex); 682 | } catch (...) { 683 | collector_t::get_instance()->on_exception(); 684 | } 685 | } 686 | 687 | /* The name of the fixture. Never null or empty. */ 688 | const char *get_name() const noexcept { 689 | assert(this); 690 | return name; 691 | } 692 | 693 | /* TODO */ 694 | void report(std::ostream &strm, const config_t &config, bool ok) const { 695 | assert(this); 696 | assert(&strm); 697 | assert(&config); 698 | if (ok) { 699 | strm << config.green() 700 | << "[ " << std::setw(4) << line << ", pass: " << name << " ]" 701 | << config.plain() << std::endl; 702 | } else { 703 | strm << config.red() 704 | << "[ " << std::setw(4) << line << ", fail: " << name << " ]" 705 | << config.plain() << std::endl; 706 | } 707 | } 708 | 709 | private: 710 | 711 | /* Our node in the static linked list of instances of fixtures. */ 712 | static_list_node_t node; 713 | 714 | /* The line number within the source at which this fixture is defined. */ 715 | int line; 716 | 717 | /* See accessor. */ 718 | const char *name; 719 | 720 | /* The function to run for this fixture. Never null. */ 721 | func_t func; 722 | 723 | }; // fixture_t 724 | 725 | /* TODO */ 726 | using fixtures_t = fixture_t::static_list_node_t; 727 | 728 | /* TODO */ 729 | class driver_t final { 730 | public: 731 | 732 | /* No copying or moving. */ 733 | driver_t(const driver_t &) = delete; 734 | driver_t &operator=(const driver_t &) = delete; 735 | 736 | /* TODO */ 737 | static int drive(const config_t &config) { 738 | return driver_t(config)(); 739 | } 740 | 741 | private: 742 | 743 | /* TODO */ 744 | explicit driver_t(const config_t &config) 745 | : config(config), passed(0), failed(0), skipped(0) {} 746 | 747 | /* TODO */ 748 | ~driver_t() {} 749 | 750 | /* Visit each fixture with on_fixture(), counting the number passed, 751 | failed, and skipped. Report to cout and/or cerr as specified in the 752 | config. Return an OS exit code. */ 753 | int operator()() { 754 | assert(this); 755 | /* Visit each fixture with on_fixture(), counting the number passed, 756 | failed, and skipped. Report on each as per config. */ 757 | fixtures_t::for_each_instance( 758 | [this](const fixture_t *fixture) { 759 | on_fixture(fixture); 760 | return true; 761 | } 762 | ); 763 | auto *strm = config.get_summary_strm(); 764 | /* Write the summary report, as per config. */ 765 | if (strm) { 766 | if (passed) { 767 | (*strm) << config.green(); 768 | } 769 | (*strm) << "passed: " << passed << config.plain() << ", "; 770 | if (failed) { 771 | (*strm) << config.red(); 772 | } 773 | (*strm) << "failed: " << failed << config.plain() 774 | << ", skipped: " << skipped << std::endl; 775 | } 776 | /* Return failure only if at least one fixture failed. This means that 777 | set of no fixtures (or no fixtures matching the regex) never fails. */ 778 | return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 779 | } 780 | 781 | /* TODO */ 782 | void on_fixture(const fixture_t *fixture) { 783 | assert(this); 784 | assert(fixture); 785 | /* If we're limited fixtures with names matching a regex and this one 786 | doesn't match, skip it. */ 787 | if (config.fixture_name_regex && 788 | !std::regex_match(fixture->get_name(), *config.fixture_name_regex)) { 789 | ++skipped; 790 | return; 791 | } 792 | /* Construct an outcome collector and run the fixture, noting whether it 793 | passed or failed. */ 794 | collector_t collector; 795 | (*fixture)(); 796 | const config_t::channel_t *channel; 797 | if (collector) { 798 | channel = &config.pass_channel; 799 | ++passed; 800 | } else { 801 | channel = &config.fail_channel; 802 | ++failed; 803 | } 804 | /* Write the fixture report, as per config. */ 805 | auto *strm = channel->get_strm(); 806 | if (strm) { 807 | switch (channel->style) { 808 | case config_t::terse: { 809 | fixture->report(*strm, config, collector); 810 | break; 811 | } 812 | case config_t::verbose: { 813 | fixture->report(*strm, config, collector); 814 | collector.report(*strm, config); 815 | break; 816 | } 817 | } 818 | } 819 | } 820 | 821 | /* TODO */ 822 | const config_t &config; 823 | 824 | /* TODO */ 825 | size_t passed, failed, skipped; 826 | 827 | }; // driver_t 828 | 829 | /* TODO */ 830 | class parser_t final { 831 | public: 832 | 833 | /* No copying or moving. */ 834 | parser_t(const parser_t &) = delete; 835 | parser_t &operator=(const parser_t &) = delete; 836 | 837 | /* TODO */ 838 | using msgs_t = std::vector; 839 | 840 | /* TODO */ 841 | class error_t final 842 | : public std::exception { 843 | public: 844 | 845 | /* TODO */ 846 | error_t(msgs_t &&msgs) 847 | : msgs(std::move(msgs)) { 848 | assert(!this->msgs.empty()); 849 | } 850 | 851 | /* TODO */ 852 | virtual const char *what() const noexcept { 853 | assert(this); 854 | if (what_cache.empty()) { 855 | std::ostringstream strm; 856 | report(strm, " ", ", "); 857 | what_cache = strm.str(); 858 | } 859 | return what_cache.c_str(); 860 | } 861 | 862 | /* TODO */ 863 | void report( 864 | std::ostream &strm, 865 | const char *first_sep, const char *other_seps) const { 866 | assert(this); 867 | assert(first_sep); 868 | assert(other_seps); 869 | strm << "error(s) parsing config:"; 870 | const char *sep = first_sep; 871 | for (const auto &msg: msgs) { 872 | strm << sep << msg; 873 | sep = other_seps; 874 | } 875 | } 876 | 877 | private: 878 | 879 | /* TODO */ 880 | msgs_t msgs; 881 | 882 | /* TODO */ 883 | mutable std::string what_cache; 884 | 885 | }; // parser_t::error_t 886 | 887 | /* TODO */ 888 | static std::unique_ptr parse(int argc, char *argv[]) { 889 | return parser_t(argc, argv)(); 890 | } 891 | 892 | private: 893 | 894 | /* Conditional compilation a-go-go! When it comes to identifying a 895 | command line option, Windows goes its own way. */ 896 | #ifdef __WIN32__ 897 | static constexpr char opt_intro = '/'; 898 | #else 899 | static constexpr char opt_intro = '-'; 900 | #endif 901 | 902 | /* TODO */ 903 | using opt_handler_t = void (parser_t::*)(char, const char *); 904 | 905 | /* Borrow these types from config_t. */ 906 | using output_t = config_t::output_t; 907 | using style_t = config_t::style_t; 908 | 909 | /* TODO */ 910 | class msg_logger_t final { 911 | public: 912 | 913 | /* No copying or moving. */ 914 | msg_logger_t(const msg_logger_t &) = delete; 915 | msg_logger_t &operator=(const msg_logger_t &) = delete; 916 | 917 | /* TODO */ 918 | explicit msg_logger_t(parser_t *parser) 919 | : parser(parser) { 920 | assert(parser); 921 | } 922 | 923 | /* TODO */ 924 | ~msg_logger_t() { 925 | assert(this); 926 | parser->msgs.emplace_back(strm.str()); 927 | } 928 | 929 | /* TODO */ 930 | std::ostream &operator*() { 931 | assert(this); 932 | return strm; 933 | } 934 | 935 | private: 936 | 937 | /* TODO */ 938 | parser_t *parser; 939 | 940 | /* TODO */ 941 | std::ostringstream strm; 942 | 943 | }; // config_t::msg_logger_t; 944 | 945 | /* TODO */ 946 | parser_t(int argc, char *argv[]) 947 | : opt_handlers({ 948 | { 'c', &parser_t::on_color_opt }, 949 | { 'f', &parser_t::on_fail_opt }, 950 | { 'n', &parser_t::on_name_opt }, 951 | { 'p', &parser_t::on_pass_opt }, 952 | { 's', &parser_t::on_summary_opt } 953 | }), args_cursor(argv), args_limit(argv + argc) {} 954 | 955 | /* TODO */ 956 | ~parser_t() {} 957 | 958 | /* TODO */ 959 | std::unique_ptr operator()() { 960 | assert(this); 961 | /* The first arg is the name of the program. */ 962 | std::unique_ptr config(new config_t);; 963 | config->prog_name = pop_arg(); 964 | /* Process the remaining args. */ 965 | bool ignoring_opts = false; 966 | for (;;) { 967 | /* Pop the next arg. If there isn't one, we're done. */ 968 | const char *arg = pop_arg(); 969 | if (!arg) { 970 | break; 971 | } 972 | /* Skip an empty arg, if we somehow got one. */ 973 | if (!*arg) { 974 | continue; 975 | } 976 | /* We don't take positional args, so any non-opt arg is just junk. */ 977 | if (ignoring_opts || *arg != opt_intro) { 978 | on_arg(arg); 979 | continue; 980 | } 981 | /* Skip any repeats of the opt intro char. */ 982 | do { 983 | ++arg; 984 | } while (*arg && *arg == opt_intro); 985 | /* If the arg was nothing but intro chars, it means to treat the rest 986 | of the args as non-opts. */ 987 | char opt = *arg++; 988 | if (!opt) { 989 | ignoring_opts = true; 990 | continue; 991 | } 992 | /* If this is a cry for help, show help and bail out of parsing. */ 993 | if (opt == '?') { 994 | show_usage(*config); 995 | return nullptr; 996 | } 997 | /* Look up the handler for the opt. */ 998 | auto iter = opt_handlers.find(opt); 999 | if (iter != opt_handlers.end()) { 1000 | /* Skip any equals signs after the opt char. */ 1001 | for (; *arg && *arg == '='; ++arg); 1002 | /* Hand-off to the handler. */ 1003 | (this->*(iter->second))(opt, arg); 1004 | } else { 1005 | /* No handler? This opt is bogus. */ 1006 | (*msg_logger_t(this)) << "unknown option '" << opt << '\''; 1007 | } 1008 | } // for 1009 | /* If we logged any error messages, package them up into an exception 1010 | and throw. */ 1011 | if (!msgs.empty()) { 1012 | throw error_t(std::move(msgs)); 1013 | } 1014 | /* Fill in the config with the opts we've parsed. */ 1015 | if (use_color) { 1016 | config->use_color = *use_color; 1017 | } 1018 | config->fixture_name_regex = std::move(fixture_name_regex); 1019 | return std::move(config); 1020 | } 1021 | 1022 | /* TODO */ 1023 | void log_conflicting_opt(char opt) { 1024 | assert(this); 1025 | (*msg_logger_t(this)) << "conflicting option '" << opt << '\''; 1026 | } 1027 | 1028 | /* TODO */ 1029 | void on_arg(const char *arg) { 1030 | assert(this); 1031 | assert(arg); 1032 | (*msg_logger_t(this)) << "extra argument \"" << arg << '"'; 1033 | } 1034 | 1035 | /* TODO */ 1036 | void on_color_opt(char opt, const char *arg) { 1037 | assert(this); 1038 | arg = try_get_opt_arg(opt, arg, false); 1039 | try_parse_bool_opt("color", arg, use_color); 1040 | } 1041 | 1042 | /* TODO */ 1043 | void on_fail_opt(char opt, const char *arg) { 1044 | assert(this); 1045 | arg = try_get_opt_arg(opt, arg); 1046 | if (!arg) { 1047 | return; 1048 | } 1049 | } 1050 | 1051 | /* TODO */ 1052 | void on_name_opt(char opt, const char *arg) { 1053 | assert(this); 1054 | arg = try_get_opt_arg(opt, arg); 1055 | if (!arg) { 1056 | return; 1057 | } 1058 | if (fixture_name_regex) { 1059 | log_conflicting_opt(opt); 1060 | return; 1061 | } 1062 | try { 1063 | fixture_name_regex.reset(new std::regex(arg)); 1064 | } catch (const std::exception &) { 1065 | (*msg_logger_t(this)) 1066 | << "bad fixture name pattern \"" << arg << '"'; 1067 | } 1068 | } 1069 | 1070 | /* TODO */ 1071 | void on_pass_opt(char opt, const char *arg) { 1072 | assert(this); 1073 | arg = try_get_opt_arg(opt, arg); 1074 | if (!arg) { 1075 | return; 1076 | } 1077 | } 1078 | 1079 | /* TODO */ 1080 | void on_summary_opt(char opt, const char *arg) { 1081 | assert(this); 1082 | arg = try_get_opt_arg(opt, arg); 1083 | if (!arg) { 1084 | return; 1085 | } 1086 | } 1087 | 1088 | /* TODO */ 1089 | const char *pop_arg() { 1090 | assert(this); 1091 | return (args_cursor < args_limit) ? *args_cursor++ : nullptr; 1092 | } 1093 | 1094 | /* TODO */ 1095 | static void show_usage(const config_t &config) { 1096 | assert(&config); 1097 | std::cout << "usage: " << config.prog_name << std::endl; 1098 | } 1099 | 1100 | /* TODO */ 1101 | const char *try_get_opt_arg( 1102 | char opt, const char *arg, bool is_required = true) { 1103 | assert(this); 1104 | /* If we already have a non-empty arg string, use it. */ 1105 | if (arg && *arg) { 1106 | return arg; 1107 | } 1108 | /* Peek at the next arg, if there is one. If it's a non-arg, we'll 1109 | use it. */ 1110 | if (args_cursor < args_limit && **args_cursor != opt_intro) { 1111 | return *args_cursor++; 1112 | } 1113 | /* There's no arg available. If one was required, log the error. */ 1114 | if (is_required) { 1115 | (*msg_logger_t(this)) 1116 | << "missing argument for option '" << opt << '\''; 1117 | } 1118 | return nullptr; 1119 | } 1120 | 1121 | /* TODO */ 1122 | bool try_parse_bool_opt( 1123 | const char *desc, 1124 | const char *&opt, std::unique_ptr &ptr) { 1125 | assert(this); 1126 | assert(desc); 1127 | assert(&ptr); 1128 | bool temp, success; 1129 | if (opt && *opt) { 1130 | switch (*opt) { 1131 | case 't': { 1132 | temp = true; 1133 | success = true; 1134 | break; 1135 | } 1136 | case 'f': { 1137 | temp = false; 1138 | success = true; 1139 | break; 1140 | } 1141 | default: { 1142 | temp = false; 1143 | success = false; 1144 | } 1145 | } 1146 | } else { 1147 | temp = true; 1148 | success = true; 1149 | } 1150 | if (success) { 1151 | if (!ptr) { 1152 | ptr.reset(new bool(temp)); 1153 | } else if (*ptr != temp) { 1154 | (*msg_logger_t(this)) 1155 | << "conflicting " << desc << " option"; 1156 | } 1157 | ++opt; 1158 | } 1159 | return success; 1160 | } 1161 | 1162 | /* TODO */ 1163 | bool try_parse_output_opt( 1164 | const char *desc, 1165 | const char *&opt, std::unique_ptr &ptr) { 1166 | assert(this); 1167 | assert(desc); 1168 | assert(&opt); 1169 | assert(&ptr); 1170 | output_t temp; 1171 | bool success; 1172 | switch (*opt) { 1173 | case 'o': { 1174 | temp = config_t::to_stdout; 1175 | success = true; 1176 | break; 1177 | } 1178 | case 'e': { 1179 | temp = config_t::to_stderr; 1180 | success = true; 1181 | break; 1182 | } 1183 | case 'x': { 1184 | temp = config_t::to_nowhere; 1185 | success = true; 1186 | break; 1187 | } 1188 | default: { 1189 | temp = config_t::to_nowhere; 1190 | success = false; 1191 | } 1192 | } 1193 | if (success) { 1194 | if (!ptr) { 1195 | ptr.reset(new output_t(temp)); 1196 | } else if (*ptr != temp) { 1197 | (*msg_logger_t(this)) 1198 | << "conflicting " << desc << " option '" << *opt << '\''; 1199 | } 1200 | ++opt; 1201 | } 1202 | return success; 1203 | } 1204 | 1205 | /* TODO */ 1206 | bool try_parse_style_opt( 1207 | const char *desc, 1208 | const char *&opt, std::unique_ptr &ptr) { 1209 | assert(this); 1210 | assert(desc); 1211 | assert(&opt); 1212 | assert(&ptr); 1213 | style_t temp; 1214 | bool success; 1215 | switch (*opt) { 1216 | case 't': { 1217 | temp = config_t::terse; 1218 | success = true; 1219 | break; 1220 | } 1221 | case 'v': { 1222 | temp = config_t::verbose; 1223 | success = true; 1224 | break; 1225 | } 1226 | default: { 1227 | temp = config_t::verbose; 1228 | success = false; 1229 | } 1230 | } 1231 | if (success) { 1232 | if (!ptr) { 1233 | ptr.reset(new style_t(temp)); 1234 | } else if (*ptr != temp) { 1235 | (*msg_logger_t(this)) 1236 | << "conflicting " << desc << " option '" << *opt << '\''; 1237 | } 1238 | ++opt; 1239 | } 1240 | return success; 1241 | } 1242 | 1243 | /* TODO */ 1244 | const std::map opt_handlers; 1245 | 1246 | /* TODO */ 1247 | char **args_cursor, **args_limit; 1248 | 1249 | /* TODO */ 1250 | msgs_t msgs; 1251 | 1252 | /* TODO */ 1253 | struct channel_t final { 1254 | 1255 | /* TODO */ 1256 | std::unique_ptr output; 1257 | 1258 | /* TODO */ 1259 | std::unique_ptr style; 1260 | 1261 | } pass_channel, fail_channel; 1262 | 1263 | /* TODO */ 1264 | std::unique_ptr summary_output; 1265 | 1266 | /* TODO */ 1267 | std::unique_ptr use_color; 1268 | 1269 | /* TODO */ 1270 | std::unique_ptr fixture_name_regex; 1271 | 1272 | }; // parser_t 1273 | 1274 | /* TODO */ 1275 | inline int main(int argc, char *argv[]) { 1276 | int result; 1277 | try { 1278 | auto config = parser_t::parse(argc, argv); 1279 | result = config ? driver_t::drive(*config) : EXIT_SUCCESS; 1280 | } catch (const parser_t::error_t &error) { 1281 | error.report(std::cerr, "\n ", "\n "); 1282 | std::cerr << std::endl; 1283 | result = EXIT_FAILURE; 1284 | } 1285 | return result; 1286 | } 1287 | 1288 | } // lick 1289 | } // cppcon14 1290 | 1291 | /* Inserts arbitrary types of data into the description of an expectation 1292 | object. Expectation objects are temporary, so we use an r-value reference 1293 | here. */ 1294 | template 1295 | inline cppcon14::lick::expect_t &&operator<<( 1296 | cppcon14::lick::expect_t &&expectation, const that_t &that) { 1297 | assert(&expectation); 1298 | expectation.get_strm() << that; 1299 | return std::move(expectation); 1300 | } 1301 | -------------------------------------------------------------------------------- /shapes1.h: -------------------------------------------------------------------------------- 1 | struct pnt_t final { 2 | double x, y; 3 | }; 4 | 5 | struct shape_t {}; 6 | 7 | struct circle_t final : shape_t { 8 | pnt_t ctr; 9 | double radius; 10 | }; 11 | 12 | struct square_t final : shape_t { 13 | pnt_t ctr; 14 | double size; 15 | }; 16 | 17 | struct triangle_t final : shape_t { 18 | pnt_t pnts[3]; 19 | }; 20 | -------------------------------------------------------------------------------- /shapes2.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct pnt_t final { 4 | double x, y; 5 | }; 6 | 7 | struct shape_t { 8 | virtual double get_area() const = 0; 9 | virtual double get_perimeter() const = 0; 10 | virtual void write(std::ostream &strm) const = 0; 11 | }; 12 | 13 | struct circle_t final : shape_t { 14 | virtual double get_area() const override; 15 | virtual double get_perimeter() const override; 16 | virtual void write(std::ostream &strm) const override; 17 | pnt_t ctr; 18 | double radius; 19 | }; 20 | 21 | struct square_t final : shape_t { 22 | virtual double get_area() const override; 23 | virtual double get_perimeter() const override; 24 | virtual void write(std::ostream &strm) const override; 25 | pnt_t ctr; 26 | double size; 27 | }; 28 | 29 | struct triangle_t final : shape_t { 30 | virtual double get_area() const override; 31 | virtual double get_perimeter() const override; 32 | virtual void write(std::ostream &strm) const override; 33 | pnt_t pnts[3]; 34 | }; 35 | -------------------------------------------------------------------------------- /shapes3.h: -------------------------------------------------------------------------------- 1 | struct pnt_t final { 2 | double x, y; 3 | }; 4 | 5 | struct shape_t { 6 | struct visitor_t; 7 | virtual void accept(const visitor_t &) const = 0; 8 | }; 9 | 10 | struct circle_t final : shape_t { 11 | pnt_t ctr; 12 | double radius; 13 | virtual void accept(const visitor_t &) const override; 14 | }; 15 | 16 | struct square_t final : shape_t { 17 | pnt_t ctr; 18 | double size; 19 | virtual void accept(const visitor_t &) const override; 20 | }; 21 | 22 | struct triangle_t final : shape_t { 23 | pnt_t pnts[3]; 24 | virtual void accept(const visitor_t &) const override; 25 | }; 26 | 27 | // can't define this until all the finals are defined 28 | struct shape_t::visitor_t final { 29 | virtual void operator()(const circle_t &) const = 0; 30 | virtual void operator()(const square_t &) const = 0; 31 | virtual void operator()(const triangle_t &) const = 0; 32 | }; 33 | 34 | // can't define these until the visitor is defined 35 | void circle_t::accept(const visitor_t &visitor) const { visitor(this); } 36 | void square_t::accept(const visitor_t &visitor) const { visitor(this); } 37 | void triangle_t::accept(const visitor_t &visitor) const { visitor(this); } 38 | 39 | inline double get_area(const shape_t *shape) { 40 | struct visitor_t final : shape_t::visitor_t { 41 | double *result; 42 | virtual void operator()(const circle_t &) const override { 43 | *result = /* pi-r-squared */ 0; 44 | } 45 | virtual void operator()(const square_t &) const override { 46 | *result = /* size-squared */ 0; 47 | } 48 | virtual void operator()(const triangle_t &) const override { 49 | *result = /* half-base-height */ 0; 50 | } 51 | }; 52 | double result; 53 | shape->accept(visitor_t { &result }); 54 | return result; 55 | } 56 | 57 | inline double get_perimeter(const shape_t *shape) { 58 | struct visitor_t final : shape_t::visitor_t { 59 | double *result; 60 | virtual void operator()(const circle_t &) const override { 61 | *result = /* pi-diameter */ 0; 62 | } 63 | virtual void operator()(const square_t &) const override { 64 | *result = /* 4-size */ 0; 65 | } 66 | virtual void operator()(const triangle_t &) const override { 67 | *result = /* sum-of-edges */ 0; 68 | } 69 | }; 70 | double result; 71 | shape->accept(visitor_t { &result }); 72 | return result; 73 | } 74 | 75 | inline std::ostream &operator<<(std::ostream &strm, const shape_t *shape) { 76 | struct visitor_t final : shape_t::visitor_t { 77 | std::ostream *strm; 78 | virtual void operator()(const circle_t &that) const override { 79 | (*strm) << that.ctr << ", " << that.radius; 80 | } 81 | virtual void operator()(const square_t &that) const override { 82 | (*strm) << that.ctr << ", " that.size; 83 | } 84 | virtual void operator()(const triangle_t &that) const override { 85 | (*strm) << that.pnts[0] << ", " 86 | << that.pnts[1] << ", " 87 | << that.pnts[2]; 88 | } 89 | }; 90 | shape->accept(visitor_t { &strm }); 91 | return strm; 92 | } 93 | -------------------------------------------------------------------------------- /shapes4.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "variant.h" 4 | 5 | struct pnt_t final { 6 | double x, y; 7 | }; 8 | 9 | std::ostream &operator<<(std::ostream &strm, const pnt_t &that) { 10 | return strm << '[' << that.x << ", " << that.y << ']'; 11 | } 12 | 13 | struct circle_t final { 14 | pnt_t ctr; 15 | double radius; 16 | }; 17 | 18 | struct square_t final { 19 | pnt_t ctr; 20 | double size; 21 | }; 22 | 23 | struct triangle_t final { 24 | pnt_t pnts[3]; 25 | }; 26 | 27 | using shape_t = cppcon14::variant::variant_t; 28 | 29 | double get_area(const shape_t &shape) { 30 | return cppcon14::variant::match( 31 | shape, 32 | [](const circle_t &) { 33 | return /* pi-r-squared */ 0; 34 | }, 35 | [](const square_t &) { 36 | return /* size-squared */ 0; 37 | }, 38 | [] (const triangle_t &) { 39 | return /* half-base-height */ 0; 40 | } 41 | ); 42 | } 43 | 44 | double get_perimeter(const shape_t &shape) { 45 | return cppcon14::variant::match( 46 | shape, 47 | [](const circle_t &) { 48 | return /* pi-diameter */ 0; 49 | }, 50 | [](const square_t &) { 51 | return /* 4-size */ 0; 52 | }, 53 | [] (const triangle_t &) { 54 | return /* sum-of-edges*/ 0; 55 | } 56 | ); 57 | } 58 | 59 | std::ostream &operator<<(std::ostream &strm, const shape_t &shape) { 60 | cppcon14::variant::match( 61 | shape, 62 | [&](const circle_t &that) { 63 | strm << that.ctr << ", " << that.radius; 64 | }, 65 | [&](const square_t &that) { 66 | strm << that.ctr << ", " << that.size; 67 | }, 68 | [&](const triangle_t &that) { 69 | strm << that.pnts[0] << ", " 70 | << that.pnts[1] << ", " 71 | << that.pnts[2]; 72 | } 73 | ); 74 | return strm; 75 | } 76 | -------------------------------------------------------------------------------- /speed.test.cc: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | TODO 17 | --------------------------------------------------------------------------- */ 18 | 19 | #include 20 | #include 21 | 22 | #include "variant.h" 23 | 24 | #include 25 | 26 | using namespace std; 27 | using namespace cppcon14::variant; 28 | 29 | size_t n = 90000000; 30 | 31 | auto virtual_dispatch() { 32 | struct shape_t { 33 | virtual double get_area() const = 0; 34 | }; 35 | struct circle_t : shape_t { 36 | circle_t(double radius) : radius(radius) {} 37 | virtual double get_area() const { return 3.14 * radius * radius; } 38 | double radius; 39 | }; 40 | struct square_t : shape_t { 41 | square_t(double side) : side(side) {} 42 | virtual double get_area() const { return side * side; } 43 | double side; 44 | }; 45 | struct triangle_t : shape_t { 46 | triangle_t(double base, double height) : base(base), height(height) {} 47 | virtual double get_area() const { return base * height / 2; } 48 | double base; 49 | double height; 50 | }; 51 | std::vector> shapes; 52 | shapes.reserve(n); 53 | for (size_t i = 0; i < n / 3; ++i) { 54 | shapes.push_back(std::make_unique(101)); 55 | } // for 56 | for (size_t i = 0; i < n / 3; ++i) { 57 | shapes.push_back(std::make_unique(101)); 58 | } // for 59 | for (size_t i = 0; i < n / 3; ++i) { 60 | shapes.push_back(std::make_unique(101, 202)); 61 | } // for 62 | std::vector results; 63 | results.reserve(n); 64 | auto start = std::chrono::steady_clock::now(); 65 | for (size_t i = 0; i < shapes.size(); ++i) { 66 | results[i] = shapes[i]->get_area(); 67 | } // for 68 | auto end = std::chrono::steady_clock::now(); 69 | return end - start; 70 | } 71 | 72 | struct circle_t { 73 | circle_t(double radius) : radius(radius) {} 74 | double get_area() const { return 3.14 * radius * radius; } 75 | double radius; 76 | }; 77 | struct square_t { 78 | square_t(double side) : side(side) {} 79 | double get_area() const { return side * side; } 80 | double side; 81 | }; 82 | struct triangle_t { 83 | triangle_t(double base, double height) : base(base), height(height) {} 84 | double get_area() const { return base * height / 2; } 85 | double base; 86 | double height; 87 | }; 88 | 89 | struct boost_get_area_t : boost::static_visitor { 90 | template 91 | double operator()(const T &t) const { return t.get_area(); } 92 | }; 93 | 94 | auto boost_variant() { 95 | using shape_t = boost::variant; 96 | auto get_area = boost_get_area_t(); 97 | std::vector shapes; 98 | shapes.reserve(n); 99 | for (size_t i = 0; i < n / 3; ++i) { 100 | shapes.push_back(circle_t(101)); 101 | } // for 102 | for (size_t i = 0; i < n / 3; ++i) { 103 | shapes.push_back(square_t(101)); 104 | } // for 105 | for (size_t i = 0; i < n / 3; ++i) { 106 | shapes.push_back(triangle_t(101, 202)); 107 | } // for 108 | std::vector results; 109 | results.reserve(n); 110 | auto start = std::chrono::steady_clock::now(); 111 | for (size_t i = 0; i < shapes.size(); ++i) { 112 | results[i] = boost::apply_visitor(get_area, shapes[i]); 113 | } // for 114 | auto end = std::chrono::steady_clock::now(); 115 | return end - start; 116 | } 117 | 118 | struct get_area_t { 119 | using ret_t = double; 120 | template 121 | double operator()(const T &that) { return that.get_area(); } 122 | }; 123 | 124 | auto variant() { 125 | using shape_t = variant_t; 126 | auto get_area = get_area_t(); 127 | std::vector shapes; 128 | shapes.reserve(n); 129 | for (size_t i = 0; i < n / 3; ++i) { 130 | shapes.push_back(circle_t(101)); 131 | } // for 132 | for (size_t i = 0; i < n / 3; ++i) { 133 | shapes.push_back(square_t(101)); 134 | } // for 135 | for (size_t i = 0; i < n / 3; ++i) { 136 | shapes.push_back(triangle_t(101, 202)); 137 | } // for 138 | std::vector results; 139 | results.reserve(n); 140 | auto start = std::chrono::steady_clock::now(); 141 | for (size_t i = 0; i < shapes.size(); ++i) { 142 | results[i] = apply(get_area, shapes[i]); 143 | } // for 144 | auto end = std::chrono::steady_clock::now(); 145 | return end - start; 146 | } 147 | 148 | int main() { 149 | std::cout << "took: " << std::chrono::duration_cast( 150 | virtual_dispatch()).count() << std::endl; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /transport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // --------------------------------------------------------------------------- 4 | // "car.h" 5 | 6 | struct car_t { 7 | // stuff about going fast and carrying stuff stuff in the trunk 8 | }; 9 | // --------------------------------------------------------------------------- 10 | 11 | 12 | // --------------------------------------------------------------------------- 13 | // "plane.h" 14 | 15 | struct plane_t { 16 | // stuff about going really, really fast and carrying cargo 17 | }; 18 | // --------------------------------------------------------------------------- 19 | 20 | 21 | // --------------------------------------------------------------------------- 22 | // "horse.h" 23 | 24 | struct horse_t { 25 | // stuff about neighing, running fast, and carrying people and cargo 26 | }; 27 | // --------------------------------------------------------------------------- 28 | 29 | 30 | // --------------------------------------------------------------------------- 31 | // "transport.h" 32 | 33 | /* 34 | #include "car.h" 35 | #include "plane.h" 36 | #include "horse.h" 37 | */ 38 | 39 | using transport_t = cppcon14::variant::variant_t; 40 | // stuff about going fast and carrying cargo 41 | // --------------------------------------------------------------------------- 42 | -------------------------------------------------------------------------------- /transport_animal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "animal.h" 4 | #include "transport.h" 5 | 6 | bool can_transport_animal(const transport_t &transport, 7 | const animal_t &animal) { 8 | return cppcon14::variant::match( 9 | transport, animal, 10 | [](const car_t &, const horse_t &) { 11 | // horse is too big for a car. 12 | return false; 13 | }, 14 | [](const plane_t &, const auto &) { 15 | // don't take animals onto planes. 16 | return false; 17 | }, 18 | [](const horse_t &, const horse_t &) { 19 | // don't put a horse on a horse! that's dumb. 20 | return false; 21 | }, 22 | [](const auto &, const auto &) { 23 | // you're fine. 24 | return true; 25 | }); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /transport_animal.test.cc: -------------------------------------------------------------------------------- 1 | #include "transport_animal.h" 2 | #include "variant.h" 3 | 4 | #include "lick.h" 5 | 6 | FIXTURE(transport_animal) { 7 | transport_t transport = car_t(); 8 | animal_t animal = dog_t(); 9 | EXPECT_TRUE(can_transport_animal(transport, animal)); 10 | animal = horse_t(); 11 | EXPECT_FALSE(can_transport_animal(transport, animal)); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /val.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | TODO 17 | --------------------------------------------------------------------------- */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "variant.h" 26 | 27 | namespace cppcon14 { 28 | namespace calc { 29 | 30 | /* --------------------------------------------------------------------------- 31 | TODO 32 | --------------------------------------------------------------------------- */ 33 | 34 | /* Forward-declaration of the expression type, for the benefit of lambda_t. */ 35 | struct expr_t; 36 | using expr_ptr_t = std::shared_ptr; 37 | 38 | /* TODO */ 39 | struct lambda_t final { 40 | 41 | /* TODO */ 42 | using params_t = std::vector; 43 | 44 | /* TODO */ 45 | lambda_t(params_t &¶ms, const expr_ptr_t &def) 46 | : params(move(params)), def(move(def)) {} 47 | 48 | /* TODO */ 49 | params_t params; 50 | 51 | /* TODO */ 52 | expr_ptr_t def; 53 | 54 | }; // lambda_t 55 | 56 | /* TODO */ 57 | struct val_t final 58 | : variant_t { 59 | using variant_t::variant_t; 60 | }; 61 | 62 | /* --------------------------------------------------------------------------- 63 | TODO 64 | --------------------------------------------------------------------------- */ 65 | 66 | /* TODO */ 67 | struct type_mismatch_error_t final 68 | : std::runtime_error { 69 | 70 | /* TODO */ 71 | type_mismatch_error_t() 72 | : runtime_error("type mismatch error") {} 73 | 74 | }; // type_mismatch_error_t 75 | 76 | /* TODO */ 77 | struct null_value_error_t final 78 | : std::runtime_error { 79 | 80 | /* TODO */ 81 | null_value_error_t() 82 | : runtime_error("null value error") {} 83 | 84 | }; // null_value_error_t 85 | 86 | /* TODO */ 87 | struct unary_val_functor_t { 88 | 89 | /* TODO */ 90 | using fn1_t = val_t (); 91 | 92 | /* TODO */ 93 | val_t operator()(nullptr_t) const { 94 | throw null_value_error_t(); 95 | } 96 | 97 | /* TODO */ 98 | template 99 | val_t operator()(const that_t &) const { 100 | throw type_mismatch_error_t(); 101 | } 102 | 103 | }; // unary_val_functor_t 104 | 105 | /* TODO */ 106 | struct binary_val_functor_t { 107 | 108 | /* TODO */ 109 | using fn2_t = val_t (); 110 | 111 | /* TODO */ 112 | val_t operator()(nullptr_t, nullptr_t) const { 113 | throw null_value_error_t(); 114 | } 115 | 116 | /* TODO */ 117 | template 118 | val_t operator()(const lhs_t &, nullptr_t) const { 119 | throw null_value_error_t(); 120 | } 121 | 122 | /* TODO */ 123 | template 124 | val_t operator()(nullptr_t, const rhs_t &) const { 125 | throw null_value_error_t(); 126 | } 127 | 128 | /* TODO */ 129 | template 130 | val_t operator()(const lhs_t &, const rhs_t &) const { 131 | throw type_mismatch_error_t(); 132 | } 133 | 134 | }; // binary_val_functor_t 135 | 136 | /* --------------------------------------------------------------------------- 137 | TODO 138 | --------------------------------------------------------------------------- */ 139 | 140 | /* TODO */ 141 | struct neg_t final 142 | : unary_val_functor_t { 143 | using unary_val_functor_t::operator(); 144 | val_t operator()(int that) const { 145 | return -that; 146 | } 147 | }; 148 | 149 | /* TODO */ 150 | struct not_t final 151 | : unary_val_functor_t { 152 | using unary_val_functor_t::operator(); 153 | val_t operator()(int that) const { 154 | return static_cast(!that); 155 | } 156 | }; 157 | 158 | /* TODO */ 159 | struct to_int_t final 160 | : unary_val_functor_t { 161 | using unary_val_functor_t::operator(); 162 | val_t operator()(int that) const { 163 | return that; 164 | } 165 | val_t operator()(const std::string &that) const { 166 | return stoi(that); 167 | } 168 | }; 169 | 170 | /* TODO */ 171 | struct to_str_t final 172 | : unary_val_functor_t { 173 | using unary_val_functor_t::operator(); 174 | val_t operator()(int that) const { 175 | std::ostd::stringstream strm; 176 | strm << that; 177 | return strm.str(); 178 | } 179 | val_t operator()(const std::string &that) const { 180 | return that; 181 | } 182 | }; 183 | 184 | /* --------------------------------------------------------------------------- 185 | TODO 186 | --------------------------------------------------------------------------- */ 187 | 188 | /* TODO */ 189 | struct add_t final 190 | : binary_val_functor_t { 191 | using binary_val_functor_t::operator(); 192 | val_t operator()(int lhs, int rhs) const { 193 | return lhs + rhs; 194 | } 195 | val_t operator()(const std::string &lhs, const std::string &rhs) const { 196 | return lhs + rhs; 197 | } 198 | }; 199 | 200 | /* TODO */ 201 | struct mul_t final 202 | : binary_val_functor_t { 203 | using binary_val_functor_t::operator(); 204 | val_t operator()(int lhs, int rhs) const { 205 | return lhs * rhs; 206 | } 207 | val_t operator()(const std::string &lhs, int rhs) const { 208 | std::ostd::stringstream strm; 209 | for (int i = 0; i < rhs; ++i) { 210 | strm << lhs; 211 | } 212 | return strm.str(); 213 | } 214 | }; 215 | 216 | /* TODO */ 217 | struct and_t final 218 | : binary_val_functor_t { 219 | using binary_val_functor_t::operator(); 220 | val_t operator()(int lhs, int rhs) const { 221 | return static_cast(lhs && rhs); 222 | } 223 | }; 224 | 225 | /* TODO */ 226 | struct or_t final 227 | : binary_val_functor_t { 228 | using binary_val_functor_t::operator(); 229 | val_t operator()(int lhs, int rhs) const { 230 | return static_cast(lhs || rhs); 231 | } 232 | }; 233 | 234 | /* TODO */ 235 | struct lt_t final 236 | : binary_val_functor_t { 237 | using binary_val_functor_t::operator(); 238 | val_t operator()(int lhs, int rhs) const { 239 | return static_cast(lhs < rhs); 240 | } 241 | val_t operator()(const std::string &lhs, const std::string &rhs) const { 242 | return static_cast(lhs < rhs); 243 | } 244 | }; 245 | 246 | } // calc 247 | } // cppcon14 248 | -------------------------------------------------------------------------------- /variant.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | A stack-based discriminated union. 17 | 18 | See "variant.test.cc" for examples of use. 19 | --------------------------------------------------------------------------- */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace lib { 32 | 33 | /* C++17 std::apply */ 34 | template 35 | decltype(auto) apply_impl(fn_t &&fn, 36 | tuple_t &&tuple, 37 | std::index_sequence) { 38 | return std::forward(fn)(std::get(std::forward(tuple))...); 39 | } 40 | 41 | template 42 | decltype(auto) apply(fn_t &&fn, tuple_t &&tuple) { 43 | return apply_impl(std::forward(fn), 44 | std::forward(tuple), 45 | std::make_index_sequence< 46 | std::tuple_size>::value>()); 47 | } 48 | 49 | /* C++14 std::max_element. */ 50 | template 51 | constexpr ForwardIter max_element(ForwardIter begin, ForwardIter end) { 52 | if (begin == end) { 53 | return end; 54 | } // if 55 | ForwardIter largest = begin; 56 | ++begin; 57 | for (; begin != end; ++begin) { 58 | if (*largest < *begin) { 59 | largest = begin; 60 | } // if 61 | } // for 62 | return largest; 63 | } 64 | 65 | /* C++14 std::max. */ 66 | template 67 | constexpr T max(std::initializer_list elems) { 68 | assert(elems.size() > 0); 69 | return *max_element(std::begin(elems), std::end(elems)); 70 | } 71 | 72 | } // lib 73 | 74 | namespace cppcon14 { 75 | namespace variant { 76 | 77 | /* Identity type trait. Useful for succintly defining type traits. */ 78 | template 79 | struct identity { using type = T; }; 80 | 81 | /* The type that represents the null state. */ 82 | struct null_t {}; 83 | 84 | /** 85 | * The base class for all visitors to variants. 86 | **/ 87 | 88 | /* We'll define the visitor class recursively. */ 89 | template 90 | struct visitor_t; 91 | 92 | /* The base case. */ 93 | template 94 | struct visitor_t { 95 | 96 | /* Override to handle an elem_t. */ 97 | virtual void operator()(const elem_t &) const = 0; 98 | 99 | }; // visitor_t 100 | 101 | /* Each iteration of the recursive case handles one type of element. */ 102 | template 103 | struct visitor_t : visitor_t { 104 | 105 | /* Pull the operator() overloads above us into scope. */ 106 | using visitor_t::operator(); 107 | 108 | /* Override to handle an elem_t. */ 109 | virtual void operator()(const elem_t &) const = 0; 110 | 111 | }; // visitor_t 112 | 113 | /* A class that inherits from lambdas and results in an overloaded lambda. */ 114 | template 115 | struct overload_t; 116 | 117 | /* Base case. */ 118 | template 119 | struct overload_t : lambda_t { 120 | 121 | /* Used to look-up the expected return type of the lambdas. */ 122 | using ret_t = ret_t_; 123 | 124 | using lambda_t::operator(); 125 | 126 | overload_t(lambda_t lambda) : lambda_t(std::move(lambda)) {} 127 | 128 | }; // overload_t 129 | 130 | /* Recursive case. */ 131 | template 132 | struct overload_t 133 | : lambda_t, overload_t { 134 | 135 | using super_t = overload_t; 136 | 137 | using lambda_t::operator(); 138 | using super_t::operator(); 139 | 140 | /* Cache the lambdas. */ 141 | overload_t(lambda_t lambda, more_lambdas_t... more_lambdas) 142 | : lambda_t(std::move(lambda)), super_t(std::move(more_lambdas)...) {} 143 | 144 | }; // overload 145 | 146 | /* Factory function for overload. */ 147 | template 148 | auto make_overload(lambdas_t &&... lambdas) { 149 | return overload_t...>( 150 | std::forward(lambdas)...); 151 | } 152 | 153 | /** 154 | * The variant class template itself. 155 | **/ 156 | 157 | /* A stack-based discriminated union. */ 158 | template 159 | class variant_t { 160 | private: 161 | 162 | /* The members of this variant. If you get a compilation error here, 163 | you probably have duplicates types. */ 164 | struct members_t : identity... {}; 165 | 166 | /* True iff. elem_t is among our elems_t; that is, if this variant can 167 | ever contain a value of type elem_t. We use this in a few places as a 168 | predicate to std::enable_if<>. */ 169 | template 170 | using contains = std::is_base_of, members_t>; 171 | 172 | /* If we have a null state, we must have > 1 states, 173 | otherwise we need > 0 states. */ 174 | static_assert(sizeof...(elems_t) > contains::value, 175 | "We need at least 1 state, and more than 1 if we are nullable."); 176 | 177 | public: 178 | 179 | /* The type of visitor we accept. */ 180 | using visitor_t = variant::visitor_t; 181 | 182 | /* Default construct to a null state. 183 | Only provided if we are nullable. */ 184 | template ::value>> 186 | variant_t(null_t = null_t()) noexcept { tag = get_null_tag(); } 187 | 188 | /* Construct off of an element. 189 | If we cannot assume the requested type (that is, if elem_t is not among our 190 | elems_t), this constructor is disabled. */ 191 | template >::value>> 193 | variant_t(elem_t &&elem) noexcept { 194 | assert(&elem); 195 | new (data) std::decay_t(std::forward(elem)); 196 | tag = get_tag>(); 197 | } 198 | 199 | /* Move-construct, leaving the donor null if it has a null state. */ 200 | variant_t(variant_t &&that) noexcept { 201 | tag = that.tag; 202 | (tag->move_construct)(*this, std::move(that)); 203 | } 204 | 205 | /* Copy-construct, leaving the exemplar intact. */ 206 | variant_t(const variant_t &that) { 207 | (that.tag->copy_construct)(*this, that); 208 | tag = that.tag; 209 | } 210 | 211 | /* Destroy. */ 212 | virtual ~variant_t() { 213 | assert(this); 214 | (tag->destroy)(*this); 215 | } 216 | 217 | /* Returns true if we are not null, otherwise false. 218 | Only provided if null is one of the possible states. 219 | This should be marked explicit but it's not for now just for test cases. */ 220 | template 221 | /* explicit */ operator std::enable_if_t::value, bool>() const { 222 | return tag != get_null_tag(); 223 | } 224 | 225 | /* Move-assign, leaving the donor null. */ 226 | variant_t &operator=(variant_t &&that) noexcept { 227 | assert(this); 228 | assert(&that); 229 | if (this != &that) { 230 | this->~variant_t(); 231 | new (this) variant_t(std::move(that)); 232 | } // if 233 | return *this; 234 | } 235 | 236 | /* Copy-assign, leaving the exemplar intact. */ 237 | variant_t &operator=(const variant_t &that) { 238 | assert(this); 239 | assert(&that); 240 | if (this != &that) { 241 | *this = variant_t(that); 242 | } // if 243 | return *this; 244 | } 245 | 246 | /* Accept the visitor and dispatch based on our contents. */ 247 | void accept(const visitor_t &visitor) const { 248 | assert(this); 249 | (tag->accept)(*this, visitor); 250 | } 251 | 252 | /* Try to access our contents as a particular type. If we don't currently 253 | contain a value of the requested type, throw std::bad_cast. If we can 254 | never be of the requested type (that is, elem_t is not among our 255 | elems_t), this function is disabled. */ 256 | template 257 | std::enable_if_t::value, const elem_t &> as() const { 258 | assert(this); 259 | const elem_t *ptr = try_as(); 260 | if (!ptr) { 261 | throw std::bad_cast(); 262 | } // if 263 | return *ptr; 264 | } 265 | 266 | /* The std::type_info for our contents, or a null pointer if we're null. */ 267 | const std::type_info *get_type_info() const noexcept { 268 | assert(this); 269 | return (tag->get_type_info)(); 270 | } 271 | 272 | /* Be null. */ 273 | template 274 | std::enable_if_t::value, variant_t &> &reset() noexcept { 275 | assert(this); 276 | this->~variant_t(); 277 | tag = get_null_tag(); 278 | return *this; 279 | } 280 | 281 | /* Try to access our contents as a particular type. If we don't currently 282 | contain a value of the requested type, return a null pointer. If we can 283 | never be of the requested type (that is, elem_t is not among our 284 | elems_t), this function is disabled. */ 285 | template 286 | std::enable_if_t::value, const elem_t *> try_as() const { 287 | assert(this); 288 | return apply( 289 | make_overload([](const elem_t &val) { return &val; }, 290 | [](const auto &) { return nullptr; }), 291 | *this); 292 | } 293 | 294 | private: 295 | 296 | /* A variant keeps track of what state its in by keeping a pointer to an 297 | instance of this structure. Think of it as a hand-rolled vtable, 298 | containing pointers to the functions that do the work of the variant. 299 | The get_tag() and get_null_tag() functions, below, are responsible for 300 | defining the instances of this type. */ 301 | struct tag_t final { 302 | 303 | /* Move other into self and set other null. */ 304 | void (*move_construct)(variant_t &self, variant_t &&other) noexcept; 305 | 306 | /* Copy other into self. */ 307 | void (*copy_construct)(variant_t &self, const variant_t &other); 308 | 309 | /* Destroy self. */ 310 | void (*destroy)(variant_t &) noexcept; 311 | 312 | /* Accept a visitor on behalf of self. */ 313 | void (*accept)(const variant_t &self, const visitor_t &visitor); 314 | 315 | /* The std::type_info we handle, or a null pointer if we handle null. */ 316 | const std::type_info *(*get_type_info)() noexcept; 317 | 318 | }; // variant_t 319 | 320 | /* Force our storage area into type. */ 321 | template 322 | elem_t &force_as() & noexcept { 323 | assert(this); 324 | return reinterpret_cast(data); 325 | } 326 | 327 | /* Force our storage area into type. */ 328 | template 329 | elem_t &&force_as() && noexcept { 330 | assert(this); 331 | return reinterpret_cast(data); 332 | } 333 | 334 | /* Force our storage area into type. */ 335 | template 336 | const elem_t &force_as() const & noexcept { 337 | assert(this); 338 | return reinterpret_cast(data); 339 | } 340 | 341 | /* The tag we use when we contain an instance of elem_t. */ 342 | template 343 | static const tag_t *get_tag() { 344 | static tag_t tag { 345 | [](variant_t &self, variant_t &&other) { 346 | new (self.data) elem_t(std::move(other).template force_as()); 347 | make_overload( 348 | [](std::true_type, auto &&other) { std::move(other).reset(); }, 349 | [](std::false_type, auto &&) {}) 350 | (contains(), std::move(other)); 351 | }, 352 | [](variant_t &self, const variant_t &other) { 353 | new (self.data) elem_t(other.force_as()); 354 | }, // copy_construct 355 | [](variant_t &self) { self.force_as().~elem_t(); }, // destroy 356 | [](const variant_t &self, const visitor_t &visitor) { 357 | visitor(self.force_as()); 358 | }, // accept 359 | []() { return &typeid(elem_t); } // get_type_info 360 | }; 361 | return &tag; 362 | } 363 | 364 | /* The tag we use iff. we're null. */ 365 | static const tag_t *get_null_tag() { 366 | static tag_t tag { 367 | [](variant_t &, variant_t &&) {}, // move_construct 368 | [](variant_t &, const variant_t &) {}, // copy_construct 369 | [](variant_t &) {}, // destroy 370 | [](const variant_t &, const visitor_t &visitor) { 371 | visitor(null_t()); 372 | }, // accept 373 | []() -> const std::type_info * { return nullptr; } // get_type_info 374 | }; 375 | return &tag; 376 | } 377 | 378 | /* The tag which takes action for us. Never null. */ 379 | const tag_t *tag = nullptr; 380 | 381 | /* The data to be interpreted by our tag. This always passes through one 382 | of the overloads of force_as() before we use it. */ 383 | alignas(lib::max({alignof(elems_t)...})) 384 | char data[lib::max({sizeof(elems_t)...})]; 385 | 386 | }; // variant_t 387 | 388 | /* --------------------------------------------------------------------------- 389 | As we deal with applying functors (in the next two big sections), it would be 390 | handy not to have to cope with differences between those which return a value 391 | and those which return void. The following two function overloads unify the 392 | semantics of these cases by returning the result, if any, via indirection. A 393 | function which returns a value must be given a non-null pointer to a location 394 | in which to store its result. A function which returns void must be given a 395 | void pointer, which will be ignored an so can (and should) be null. 396 | --------------------------------------------------------------------------- */ 397 | 398 | template 404 | class applier_t; 405 | 406 | template 411 | class applier_t, 415 | std::tuple> : public some_visitor_t { 416 | public: 417 | 418 | applier_t(ret_t *ret, 419 | functor_t &functor, 420 | std::tuple &&members, 421 | std::tuple &&variants) 422 | : functor(functor), 423 | ret(ret), 424 | members(std::move(members)), 425 | variants(std::move(variants)) {} 426 | 427 | functor_t &functor; 428 | 429 | ret_t *ret; 430 | 431 | std::tuple members; 432 | 433 | std::tuple variants; 434 | 435 | }; // applier_t 440 | 441 | template 448 | class applier_t, 452 | std::tuple, 453 | elem_t, 454 | more_elems_t...> 455 | : public applier_t, 459 | std::tuple, 460 | more_elems_t...> { 461 | public: 462 | 463 | /* Pass everything up to the base case. */ 464 | applier_t(ret_t *ret, 465 | functor_t &functor, 466 | std::tuple &&members, 467 | std::tuple &&variants) 468 | : applier_t, 472 | std::tuple, 473 | more_elems_t...>( 474 | ret, functor, std::move(members), std::move(variants)) {} 475 | 476 | /* Override of the definition in lhs_visitor_t. This is version used 477 | when the variant is non-null. It is just a hand-off to 478 | apply_tuple(), below. */ 479 | virtual void operator()(const elem_t &elem) const override final { 480 | assert(this); 481 | dispatch(std::index_sequence_for(), 482 | elem, 483 | std::index_sequence_for()); 484 | } 485 | 486 | private: 487 | 488 | template 489 | void dispatch_impl( 490 | std::tuple &&more_members) const { 491 | make_overload( 492 | [&](auto *ret) { 493 | *ret = lib::apply(this->functor, std::move(more_members)); 494 | }, 495 | [&](void *) { lib::apply(this->functor, std::move(more_members)); }) 496 | (this->ret); 497 | } 498 | 499 | template 502 | void dispatch_impl(std::tuple &&more_members, 503 | const variant_t &variant, 504 | const more_variants_t &... more_variants) const { 505 | using applier_t = applier_t::visitor_t, 506 | ret_t, 507 | functor_t, 508 | std::tuple, 509 | std::tuple, 510 | elems_t...>; 511 | assert(&variant); 512 | variant.accept(applier_t(this->ret, 513 | this->functor, 514 | std::move(more_members), 515 | std::forward_as_tuple(more_variants...))); 516 | } 517 | 518 | template 519 | void dispatch(std::index_sequence, 520 | const elem_t &elem, 521 | std::index_sequence) const { 522 | dispatch_impl(std::forward_as_tuple(std::get(this->members)..., elem), 523 | std::get(this->variants)...); 524 | } 525 | 526 | }; // applier_t 533 | 534 | /* --------------------------------------------------------------------------- 535 | A helper for picking apart a template parameter of function type. We'll use 536 | explicit specializations to separate two cases: functions which return non- 537 | void and functions which return void. Within each, we'll define an apply() 538 | function which will apply a functor to a variant, passing zero or more 539 | additional arguments. 540 | --------------------------------------------------------------------------- */ 541 | 542 | /* Returning non-void. */ 543 | template 544 | struct storage_t { 545 | 546 | ret_t get() { return std::move(ret); } 547 | 548 | ret_t *ptr() { return &ret; } 549 | 550 | ret_t ret; 551 | 552 | }; // storage_t 553 | 554 | /* Returning void. */ 555 | template <> 556 | struct storage_t { 557 | 558 | void get() && {} 559 | 560 | void *ptr() { return nullptr; } 561 | 562 | }; // storage_t 563 | 564 | template 567 | decltype(auto) apply(functor_t &&functor, 568 | const variant_t &variant, 569 | const more_variants_t &... more_variants) { 570 | using ret_t = typename std::decay_t::ret_t; 571 | using applier_t = applier_t, 572 | ret_t, 573 | functor_t, 574 | std::tuple<>, 575 | std::tuple, 576 | elems_t...>; 577 | assert(&variant); 578 | storage_t ret; 579 | variant.accept(applier_t(ret.ptr(), 580 | functor, 581 | std::forward_as_tuple(), 582 | std::forward_as_tuple(more_variants...))); 583 | return std::move(ret).get(); 584 | } 585 | 586 | template 587 | decltype(auto) 588 | match(const variant_t &that, lambdas_t &&... lambdas) { 589 | return apply(make_overload(std::forward(lambdas)...), that); 590 | } 591 | 592 | template 596 | decltype(auto) match(const variant_t &lhs, 597 | const variant_t &rhs, 598 | lambdas_t &&... lambdas) { 599 | return apply( 600 | make_overload(std::forward(lambdas)...), lhs, rhs); 601 | } 602 | 603 | template 604 | decltype(auto) match(const std::tuple &that, 605 | lambdas_t &&... lambdas) { 606 | return lib::apply([&](auto && ... args)->decltype(auto) { 607 | return apply(make_overload( 608 | std::forward(lambdas)...), 609 | std::forward(args)...); 610 | }, 611 | that); 612 | } 613 | 614 | } // variant 615 | } // cppcon14 616 | -------------------------------------------------------------------------------- /variant.test.cc: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Copyright 2014 3 | Jason Lucas (JasonL9000@gmail.com) and 4 | Michael Park (mcypark@gmail.com). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | HTTP://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Unit tests for and examples of variant_t. 17 | --------------------------------------------------------------------------- */ 18 | 19 | #include "variant.h" 20 | 21 | #include 22 | 23 | #include "lick.h" 24 | 25 | using namespace std; 26 | using namespace cppcon14::variant; 27 | 28 | /* A union of int and string types. */ 29 | using int_or_str_or_null_t = variant_t; 30 | 31 | /* A few string constants we'll use in various text fixtures. */ 32 | static const string empty, hello("hello"), doctor("doctor"); 33 | 34 | FIXTURE(def_ctor_null) { 35 | int_or_str_or_null_t a; 36 | EXPECT_FALSE(a); 37 | } 38 | 39 | FIXTURE(move_from_val_ctor_null) { 40 | string temp = hello; 41 | int_or_str_or_null_t a = move(temp); 42 | if (EXPECT_TRUE(a)) { 43 | EXPECT_EQ(a.as(), hello); 44 | } 45 | EXPECT_TRUE(temp.empty()); 46 | } 47 | 48 | FIXTURE(copy_from_val_ctor_null) { 49 | int_or_str_or_null_t a = hello; 50 | if (EXPECT_TRUE(a)) { 51 | EXPECT_EQ(a.as(), hello); 52 | } 53 | } 54 | 55 | FIXTURE(move_ctor_null) { 56 | int_or_str_or_null_t a = hello, b = move(a); 57 | EXPECT_FALSE(a); 58 | if (EXPECT_TRUE(b)) { 59 | EXPECT_EQ(b.as(), hello); 60 | } 61 | } 62 | 63 | FIXTURE(copy_ctor_null) { 64 | int_or_str_or_null_t a = hello, b = a; 65 | if (EXPECT_TRUE(a)) { 66 | EXPECT_EQ(a.as(), hello); 67 | } 68 | if (EXPECT_TRUE(b)) { 69 | EXPECT_EQ(b.as(), hello); 70 | } 71 | } 72 | 73 | FIXTURE(move_assign_null) { 74 | int_or_str_or_null_t a = hello, b; 75 | b = move(a); 76 | EXPECT_FALSE(a); 77 | if (EXPECT_TRUE(b)) { 78 | EXPECT_EQ(b.as(), hello); 79 | } 80 | } 81 | 82 | FIXTURE(copy_assign_null) { 83 | int_or_str_or_null_t a = hello, b; 84 | b = a; 85 | if (EXPECT_TRUE(a)) { 86 | EXPECT_EQ(a.as(), hello); 87 | } 88 | if (EXPECT_TRUE(b)) { 89 | EXPECT_EQ(b.as(), hello); 90 | } 91 | } 92 | 93 | FIXTURE(get_type_info_null) { 94 | int_or_str_or_null_t a, b(101), c(hello); 95 | EXPECT_FALSE(a.get_type_info()); 96 | EXPECT_TRUE(*b.get_type_info() == typeid(int)); 97 | EXPECT_TRUE(*c.get_type_info() == typeid(string)); 98 | swap(a, b); 99 | EXPECT_TRUE(*a.get_type_info() == typeid(int)); 100 | EXPECT_FALSE(b.get_type_info()); 101 | swap(a, c); 102 | EXPECT_TRUE(*a.get_type_info() == typeid(string)); 103 | EXPECT_TRUE(*c.get_type_info() == typeid(int)); 104 | } 105 | 106 | FIXTURE(try_as_null) { 107 | int_or_str_or_null_t a, b(101), c(hello); 108 | EXPECT_FALSE(a.try_as()); 109 | EXPECT_FALSE(a.try_as()); 110 | if (EXPECT_TRUE(b.try_as())) { 111 | EXPECT_EQ(*b.try_as(), 101); 112 | } 113 | EXPECT_FALSE(b.try_as()); 114 | if (EXPECT_TRUE(c.try_as())) { 115 | EXPECT_EQ(*c.try_as(), hello); 116 | } 117 | EXPECT_FALSE(c.try_as()); 118 | } 119 | 120 | FIXTURE(reset_null) { 121 | int_or_str_or_null_t a = hello; 122 | EXPECT_TRUE(a); 123 | a.reset(); 124 | EXPECT_FALSE(a); 125 | } 126 | 127 | /* A type we'll use as a test of unary functor application. It can be applied 128 | to an instance of int_or_str_or_null_t and keeps a copy of whatever it finds there. 129 | You can examine the functor after the application to see what it found. */ 130 | struct keeper_null_t final { 131 | 132 | /* You must define a type called "fn1_t" in any structure you intend to 133 | use as a unary variant functor. The type must be a function describing 134 | your overloads of operator(). You must give the return type as well 135 | as the types of any non-variant (that is, extra) parameters taken. In 136 | this case, we return nothing and take nothing but a variant. */ 137 | using ret_t = void; 138 | 139 | /* Handle the case when the variant is null. */ 140 | void operator()(null_t) { 141 | that_int = 0; 142 | that_str.clear(); 143 | } 144 | 145 | /* Handle the case when the variant is an int. */ 146 | void operator()(int that) { 147 | that_int = that; 148 | that_str.clear(); 149 | } 150 | 151 | /* Handle the case when the variant is a string. */ 152 | void operator()(const string &that) { 153 | that_int = 0; 154 | that_str = that; 155 | } 156 | 157 | /* The int we found, or 0 if we didn't find one. */ 158 | int that_int = 0; 159 | 160 | /* The string we found, or empty if we didn't find one. */ 161 | string that_str; 162 | 163 | }; // keeper_null_t 164 | 165 | FIXTURE(keeper_null) { 166 | int_or_str_or_null_t a, b(101), c(hello); 167 | keeper_null_t keeper; 168 | keeper.that_int = 99; 169 | keeper.that_str = doctor; 170 | apply(keeper, a); 171 | EXPECT_EQ(keeper.that_int, 0); 172 | EXPECT_EQ(keeper.that_str, empty); 173 | keeper.that_int = 99; 174 | keeper.that_str = doctor; 175 | apply(keeper, b); 176 | EXPECT_EQ(keeper.that_int, 101); 177 | EXPECT_EQ(keeper.that_str, empty); 178 | keeper.that_int = 99; 179 | keeper.that_str = doctor; 180 | apply(keeper, c); 181 | EXPECT_EQ(keeper.that_int, 0); 182 | EXPECT_EQ(keeper.that_str, hello); 183 | } 184 | 185 | /* Another test of unary functor application. This one takes an extra 186 | argument and returns a string. */ 187 | struct greeter_null_t final { 188 | 189 | /* The mandatory function type definition. Here we indicate that we'll be 190 | returning a string and expect a string to be passed to use after the 191 | variant. */ 192 | using ret_t = string; 193 | 194 | /* Handle the case when the variant is null. */ 195 | string operator()(null_t) { 196 | ostringstream strm; 197 | strm << greeting << ", nobody."; 198 | return strm.str(); 199 | } 200 | 201 | /* Handle the cases when the variant is non-null. We can use a template 202 | here because we handle all the non-null cases the same way. */ 203 | template 204 | string operator()(const elem_t &elem) { 205 | ostringstream strm; 206 | strm << greeting << ", '" << elem << "'."; 207 | return strm.str(); 208 | } 209 | 210 | const string &greeting; 211 | 212 | }; // greeter_null_t 213 | 214 | FIXTURE(greeter_null) { 215 | int_or_str_or_null_t a, b(101), c(doctor); 216 | EXPECT_EQ(apply(greeter_null_t{"Hey there"}, a), "Hey there, nobody."); 217 | EXPECT_EQ(apply(greeter_null_t{"What's up"}, b), "What's up, '101'."); 218 | EXPECT_EQ(apply(greeter_null_t{"Hello"}, c), "Hello, 'doctor'."); 219 | } 220 | 221 | /* A type we'll use as a test of binary functor application. It can be 222 | applied to a pair of instances of int_or_str_or_null_t and returns a string 223 | describing that they are. */ 224 | struct pair_writer_null_t final { 225 | 226 | /* Just as the "fn1_t" type is mandatory for all unary functors, the "fn2_t" 227 | type is mandatory for all binary functors. It is similar to fn1_t but 228 | assumes we will be passed a pair of variants instead of just one. */ 229 | using ret_t = string; 230 | 231 | /* Handles all cases. */ 232 | template 233 | string operator()(const lhs_t &lhs, const rhs_t &rhs) { 234 | ostringstream strm; 235 | write(strm, lhs); 236 | strm << ", "; 237 | write(strm, rhs); 238 | return strm.str(); 239 | } 240 | 241 | /* Writes a non-null value. */ 242 | template 243 | static void write(ostream &strm, const elem_t &elem) { 244 | assert(&strm); 245 | assert(&elem); 246 | strm << elem; 247 | } 248 | 249 | /* Writes a null value. */ 250 | static void write(ostream &strm, null_t) { 251 | assert(&strm); 252 | strm << "null"; 253 | } 254 | 255 | }; // pair_writer_null_t 256 | 257 | FIXTURE(pair_writer_null) { 258 | int_or_str_or_null_t nil, lhs(101), rhs(202); 259 | EXPECT_EQ(apply(pair_writer_null_t(), lhs, rhs), "101, 202"); 260 | EXPECT_EQ(apply(pair_writer_null_t(), nil, rhs), "null, 202"); 261 | EXPECT_EQ(apply(pair_writer_null_t(), lhs, nil), "101, null"); 262 | EXPECT_EQ(apply(pair_writer_null_t(), nil, nil), "null, null"); 263 | } 264 | 265 | /** 266 | * Non-null variant. 267 | **/ 268 | 269 | /* A union of int and string types. */ 270 | using int_or_str_t = variant_t; 271 | 272 | FIXTURE(move_from_val_ctor_non_null) { 273 | string temp = hello; 274 | int_or_str_t a = move(temp); 275 | EXPECT_EQ(a.as(), hello); 276 | } 277 | 278 | FIXTURE(copy_from_val_ctor_non_null) { 279 | int_or_str_t a = hello; 280 | EXPECT_EQ(a.as(), hello); 281 | } 282 | 283 | FIXTURE(move_ctor_non_null) { 284 | int_or_str_t a = hello, b = move(a); 285 | EXPECT_EQ(b.as(), hello); 286 | } 287 | 288 | FIXTURE(copy_ctor_non_null) { 289 | int_or_str_t a = hello, b = a; 290 | EXPECT_EQ(a.as(), hello); 291 | EXPECT_EQ(b.as(), hello); 292 | } 293 | 294 | FIXTURE(move_assign_non_null) { 295 | int_or_str_t a = hello, b = 42; 296 | b = move(a); 297 | EXPECT_EQ(b.as(), hello); 298 | } 299 | 300 | FIXTURE(copy_assign_non_null) { 301 | int_or_str_t a = hello, b = 42; 302 | b = a; 303 | EXPECT_EQ(a.as(), hello); 304 | EXPECT_EQ(b.as(), hello); 305 | } 306 | 307 | FIXTURE(get_type_info_non_null) { 308 | int_or_str_t a(101), b(hello); 309 | EXPECT_TRUE(*a.get_type_info() == typeid(int)); 310 | EXPECT_TRUE(*b.get_type_info() == typeid(string)); 311 | swap(a, b); 312 | EXPECT_TRUE(*a.get_type_info() == typeid(string)); 313 | EXPECT_TRUE(*b.get_type_info() == typeid(int)); 314 | } 315 | 316 | FIXTURE(try_as_non_null) { 317 | int_or_str_t a(101), b(hello); 318 | if (EXPECT_TRUE(a.try_as())) { 319 | EXPECT_EQ(*a.try_as(), 101); 320 | } 321 | EXPECT_FALSE(a.try_as()); 322 | if (EXPECT_TRUE(b.try_as())) { 323 | EXPECT_EQ(*b.try_as(), hello); 324 | } 325 | EXPECT_FALSE(b.try_as()); 326 | } 327 | 328 | /* A type we'll use as a test of unary functor application. It can be applied 329 | to an instance of int_or_str_t and keeps a copy of whatever it finds there. 330 | You can examine the functor after the application to see what it found. */ 331 | struct keeper_non_null_t final { 332 | 333 | /* You must define a type called "fn1_t" in any structure you intend to 334 | use as a unary variant functor. The type must be a function describing 335 | your overloads of operator(). You must give the return type as well 336 | as the types of any non-variant (that is, extra) parameters taken. In 337 | this case, we return nothing and take nothing but a variant. */ 338 | using ret_t = void; 339 | 340 | /* Handle the case when the variant is an int. */ 341 | ret_t operator()(int that) { 342 | that_int = that; 343 | that_str.clear(); 344 | } 345 | 346 | /* Handle the case when the variant is a string. */ 347 | ret_t operator()(const string &that) { 348 | that_int = 0; 349 | that_str = that; 350 | } 351 | 352 | /* The int we found, or 0 if we didn't find one. */ 353 | int that_int = 0; 354 | 355 | /* The string we found, or empty if we didn't find one. */ 356 | string that_str; 357 | 358 | }; // keeper_non_null_t 359 | 360 | FIXTURE(keeper_non_null) { 361 | int_or_str_t a(101), b(hello); 362 | keeper_non_null_t keeper; 363 | keeper.that_int = 99; 364 | keeper.that_str = doctor; 365 | apply(keeper, a); 366 | EXPECT_EQ(keeper.that_int, 101); 367 | EXPECT_EQ(keeper.that_str, empty); 368 | keeper.that_int = 99; 369 | keeper.that_str = doctor; 370 | apply(keeper, b); 371 | EXPECT_EQ(keeper.that_int, 0); 372 | EXPECT_EQ(keeper.that_str, hello); 373 | } 374 | 375 | /* Another test of unary functor application. This one takes an extra 376 | argument and returns a string. */ 377 | struct greeter_non_null_t final { 378 | 379 | /* The mandatory function type definition. Here we indicate that we'll be 380 | returning a string and expect a string to be passed to use after the 381 | variant. */ 382 | using ret_t = string; 383 | 384 | /* Handle the cases when the variant is non-null. We can use a template 385 | here because we handle all the non-null cases the same way. */ 386 | template 387 | ret_t operator()(const elem_t &elem) { 388 | ostringstream strm; 389 | strm << greeting << ", '" << elem << "'."; 390 | return strm.str(); 391 | } 392 | 393 | const char *greeting; 394 | 395 | }; // greeter_non_null_t 396 | 397 | struct greeter_non_null_switch_t final { 398 | 399 | /* The mandatory function type definition. Here we indicate that we'll be 400 | returning a string and expect a string to be passed to use after the 401 | variant. */ 402 | using ret_t = string; 403 | 404 | /* Handle the cases when the variant is non-null. We can use a template 405 | here because we handle all the non-null cases the same way. */ 406 | template 407 | ret_t operator()(const elem_t &elem, const char *greeting) { 408 | ostringstream strm; 409 | strm << greeting << ", '" << elem << "'."; 410 | return strm.str(); 411 | } 412 | 413 | }; // greeter_non_null_switch_t 414 | 415 | FIXTURE(greeter_non_null) { 416 | int_or_str_t a(101), b(doctor); 417 | EXPECT_EQ(apply(greeter_non_null_t{"What's up"}, a), "What's up, '101'."); 418 | EXPECT_EQ(apply(greeter_non_null_t{"Hello"}, b), "Hello, 'doctor'."); 419 | } 420 | 421 | /* A type we'll use as a test of binary functor application. It can be 422 | applied to a pair of instances of int_or_str_t and returns a string 423 | describing that they are. */ 424 | struct pair_writer_non_null_t final { 425 | 426 | /* Just as the "fn1_t" type is mandatory for all unary functors, the "fn2_t" 427 | type is mandatory for all binary functors. It is similar to fn1_t but 428 | assumes we will be passed a pair of variants instead of just one. */ 429 | using ret_t = string; 430 | 431 | /* Handles all cases. */ 432 | template 433 | string operator()(const lhs_t &lhs, const rhs_t &rhs) { 434 | ostringstream strm; 435 | write(strm, lhs); 436 | strm << ", "; 437 | write(strm, rhs); 438 | return strm.str(); 439 | } 440 | 441 | /* Writes a non-null value. */ 442 | template 443 | static void write(ostream &strm, const elem_t &elem) { 444 | assert(&strm); 445 | assert(&elem); 446 | strm << elem; 447 | } 448 | 449 | }; // pair_writer_non_null_t 450 | 451 | FIXTURE(pair_writer_non_null) { 452 | int_or_str_t lhs(101), rhs(202); 453 | EXPECT_EQ(apply(pair_writer_non_null_t(), lhs, rhs), "101, 202"); 454 | } 455 | --------------------------------------------------------------------------------