Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Exceptional type safety

Exceptional type safety

Humouristic abuse of the C++ exception system for polymorphic return.
Presentation given at the distributed SwedenCPP Stockholm + London C++ UG meetup.

Björn Fahller

October 26, 2017
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Exceptional

    Type Safety Björn Fahller E --> E "+" E | E "-" E | "-" E | E "*" E | E "/" E | "(" E ")" | v
  2. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller std::variant<T...>

    using v = std::variant<int, std::string, bool, std::list<int>>; void func(v obj) { std::visit(overload( [](int) { /* int stuff */ }, [](std::string) { /* string stuff */ }, [](const auto&) { /* other stuff */ }), v); }
  3. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller std::variant<T...>

    using v = std::variant<int, std::string, bool, std::list<int>>; void func(v obj) { std::visit(overload( [](int) { /* int stuff */ }, [](std::string) { /* string stuff */ }, [](const auto&) { /* other stuff */ }), v); } But what if we’re writing C++11 code?
  4. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller throw/catch

    try { func(); } catch (A& a) { ... } catch (B& b) { ... } catch (...) { ... }
  5. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller throw/catch

    try { func(); } catch (A& a) { ... } catch (B& b) { ... } catch (...) { ... }
  6. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller throw/catch

    try { func(); } catch (A& a) { ... } catch (B& b) { ... } catch (...) { ... } So exceptions can give us the same desired behaviour as std::visit() does for std::variant<>
  7. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Representing

    tokens Tokens for a very simple calculator. template <char> struct C {}; struct number { double value; }; struct ident { std::string_view value; }; struct eof {}; struct remember {}; struct forget {};
  8. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Representing

    tokens Tokens for a very simple calculator. template <char> struct C {}; struct number { double value; }; struct ident { std::string_view value; }; struct eof {}; struct remember {}; struct forget {}; struct other {}; class lex { public: lex(std::string s); std::string next_token(); // throws token std::string peek(); // throws token void drop(); private: ... };
  9. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Representing

    tokens Tokens for a very simple calculator. template <char> struct C {}; struct number { double value; }; struct ident { std::string_view value; }; struct eof {}; struct remember {}; struct forget {}; struct other {}; class lex { public: lex(std::string s); std::string next_token(); // throws token std::string peek(); // throws token void drop(); private: ... }; std::string lex::scan_token() { ... switch (*iter) { case '(': ++iter;throw C<'('>{}; case ')': ++iter;throw C<')'>{}; case '/': ++iter;throw C<'/'>{}; case '*': ++iter;throw C<'*'>{}; case '=': ++iter;throw C<'='>{}; case '+': ++iter;throw C<'+'>{}; case '-': ++iter;throw C<'-'>{}; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': return scan_number(); } return "unknown"; }
  10. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Representing

    tokens Tokens for a very simple calculator. template <char> struct C {}; struct number { double value; }; struct ident { std::string_view value; }; struct eof {}; struct remember {}; struct forget {}; struct other {}; class lex { public: lex(std::string s); std::string next_token(); // throws token std::string peek(); // throws token void drop(); private: ... }; std::string lex::peek() { if (!lookahead) { try { return scan_token(); } catch (...) { lookahead = std::current_exception(); } } std::rethrow_exception(lookahead); }
  11. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; }
  12. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; }
  13. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; }
  14. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; }
  15. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer->next_token(); } catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer->next_token(); } catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; }
  16. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; } Return error messages
  17. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; } Return error messages and throw polymorphic information upp the call chain.
  18. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller double

    calculator::parse(std::string s) { lexer.reset(new lex(std::move(s))); try { lexer->peek(); } catch (remember){ lexer->drop(); return parse_remember_var();} catch (forget) { lexer->drop(); return parse_forget_var();} catch (...) { return parse_expr();} try { lexer->next_token(); } catch (eof) { } catch (...) { throw "garbage after expr";} } double calculator::parse_expr() { auto v = parse_term(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'+'>) { lexer->drop();v += parse_term(); } catch (C<'-'>) { lexer->drop();v -= parse_term(); } catch (...) { done = true; } } return v; } double calculator::parse_term() { auto v = parse_factor(); for (bool done = false; !done;) { try { lexer->peek(); } catch (C<'/'>) { lexer->drop(); v /= parse_factor(); } catch (C<'*'>) { lexer->drop(); v *= parse_factor(); } catch (...) { done = true; } } return v; } double calculator::parse_factor() { try { lexer->next_token(); } catch (ident var) { return lookup(var.value);} catch (number n) { return n.value;} catch (C<'+'>) { return parse_term(); } catch (C<'-'>) { return -parse_term(); } catch (C<'('>) { return parse_paren(); } catch (...) {} throw "unexpected"; } double calculator::parse_paren() { auto v = parse_expr(); try { lexer next_token(); } → catch (C<')'>) { return v; } catch (...) { } throw "expected ')'"; } double calculator::parse_forget_var() { try { lexer->next_token();} catch (const ident& i) { return drop_variable(i.value);} catch (...) { throw "expected variable name";} } double calculator::parse_remember_var() { std::string id; try { lexer->next_token(); } catch (ident i) { id = std::move(i.value);} catch (...) { throw "expected variable name";} try { lexer->next_token(); } catch (C<'='>) {} catch (...) { throw "expected =";} auto v = parse_expr(); memory[id] = v; return v; } Return error messages and throw polymorphic information upp the call chain.
  19. Exceptional Type Safety, C++ Stockholm 0x07, Björn Fahller @bjorn_fahller Björn

    Fahller https://github.com/rollbear/variant_parse [email protected] @bjorn_fahller @rollbear cpplang, swedencpp Exceptional Type Safety