Static modeling (& lifetime) Using types to test, document and reduce system complexity Welcome This talk/debate • Static modeling • Lifetime given time • Interrupt at any time! Safety nets • • • • • • • • • • • • Design/code reviews, pair programming Compiler Static modeling Unit tests/module tests Pipeline tests Developer testing Continuos integration Static analysis tools (PCLint etc) Dynamic analysis tools (Boundschecker, valgrind, etc) QA testing Design by contract Assertions Bold = expressed in code Fail fast • Minimize turn around time – minimize waste • Bug found => ”Possible to catch earlier?” • Review => ”Possible to catch earlier?” Failing fast in C++ Build time Code generation Macro expansion Template instantiation Translation unit compilation Linking Installation/configuration Run time Load time Static initialization Bootstrapping/Initialization code Level load ... Specific/rare situations Static modeling • Model invariants and tests with (static) types • Either compile time or instantiation time int x=-10; bounded<int, -1, 99999> i(x); quantity<speed> v = 1*meter/second; quantity<time> t = 2.0*minute; quantity<length> l = v/t; Value of static modeling 1. 2. 3. Type up ASAP - fail fast Documentation Reduces complexity / state space Code based tests Static modeling, assertions, design by contract, automated test hooks, unit tests etc. - Add ”weight” when prototyping + ”Easy” to add to existing/legacy code How to reduce the state space • Init/set/modify -> constructor/factory • Split objects into smaller pieces • Lock down dynamics ASAP Demarshalling • Type up immediately Indices • Indices only needed when de-/serializing • Look up immediately, pass around instance Use typed ids struct foo_id { int id; }; bool operator==(...); Distuinguish invalid ids by type enum error {err1, err2}; class result { bool has_id(); const /// @pre valid() foo_id get_id() const; /// @pre !valid() error get_error() const; private: int data; }; result get_id(...); // Using boost boost::variant<error, foo_id> get_id(); Optional optional<X> find_closest_x(); void do_stuff(const X& x); • Isolate inited/not inited state • Clear semantics, all-or-nothing bool find_closest_x(X& x); Right/left hand system Either compile time, runtime or combined Better granularity than math asserts on/off RAII • • • • • • • new/delete aquire/unaquire connect/disconnect lock/unlock subscribe/unsubscribe register/unregister open/close Need to match call pairs AND know if an object is initialized or not RAII example void foo(int x, int y); signal<void(int x, int y)> sig; signal_connection con(sig, foo); Side note: RAII pillars • Exceptions • Deterministic destruction Static modeling and lifetime • More work in constructor/destructor • Object lifetime becomes very important Lifetime primitives • • • • • scoped_ptr (not copyable) shared_ptr weak_ptr move_ptr (not copyable) reference (not assignable) Interfaceless objects /// @param foo is guaranteed to outlive /// return value move_ptr<void> create_stuff(const function<void()>& onBar, shared_ptr<GuiFactories> gui, const SomeFacade& foo, move_ptr<const ResourceX> x); Summary • Static modeling pros: Fails fast, documentation, minimizes total logic • Static modeling increasingly important in longer and larger projects • Static modeling increases need of clear lifetime semantics Static modeling vs. DbC Catch errors that must be checked at runtime by typing up or DbC/assertions? • Is the invariant local? • Duplicate preconditions => time to type up! • S.M. only applicable to single variables Opposite of static modeling class message_channel { void send(const string& key, const string& value); void listen(const string& key, listener& l) const; }; Dynamic modeling good for prototyping/iterating Optimal: Lock down invariants in code by typing up incrementally. No major language supports this well. Don’t get lured by ”power” • Dynamic components are very powerful but can also be abused • Specialized, less powerful = easy to understand and test, smaller state space • Think reflection, introspection, tuple spaces