|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: Misc Utilities |
| 4 | +
|
| 5 | +Author: Kurt Degiorgio |
| 6 | +
|
| 7 | +\*******************************************************************/ |
| 8 | + |
| 9 | +/// \file |
| 10 | +/// code_observer.h |
| 11 | + |
| 12 | +/// This file contains a couple of utility classes that should be used when one |
| 13 | +/// needs to read all codet's of a particular type from a given symbol |
| 14 | +/// table. This saves you from having to manually iterate through the symbol |
| 15 | +/// table. These classes essentially implement the observable pattern. |
| 16 | +/// |
| 17 | +/// In general, you first register one or more observers with |
| 18 | +/// const_code_observert. The observer is a callback that is |
| 19 | +/// associated with a specific irep_idt. This is used to determine for which |
| 20 | +/// codet's the observer will be notified. The second step involves calling |
| 21 | +/// visit_symbols which will in turn iterate through every codet inside the |
| 22 | +/// given symbol-table and notify registered observers only if the id of the |
| 23 | +/// codet in question matches the associated id of the observer. |
| 24 | +/// |
| 25 | +/// Example: Examining all codet(ID_function_call) |
| 26 | +/// |
| 27 | +/// \code |
| 28 | +/// |
| 29 | +/// void A::examine_function_calls(const codet &code) |
| 30 | +/// { |
| 31 | +/// PRECONDITION(code.get_statement()==ID_function_call); |
| 32 | +/// ... |
| 33 | +/// } |
| 34 | +/// |
| 35 | +/// ... |
| 36 | +/// |
| 37 | +/// const_code_observert code_observer; |
| 38 | +/// code_observer.register_observer(ID_function_call, |
| 39 | +/// &A::examine_function_calls, |
| 40 | +/// this, |
| 41 | +/// std::placeholders::_1); |
| 42 | +/// code_observer.visit_symbols(symbol_table); |
| 43 | +/// |
| 44 | +/// ... |
| 45 | +/// |
| 46 | +/// \endcode |
| 47 | +/// |
| 48 | +/// Note: each unique irep_idt can only be associated with one observer. |
| 49 | +/// |
| 50 | +/// Note': Currently there is only a const version of the codet observer. This |
| 51 | +/// is due to limitations with the symbol table API, specifically the lack of a |
| 52 | +/// publicly visible non-const iterator. If the codet needs to be modified then |
| 53 | +/// inside the observer callback use symbol_table_baset::get_writeable_ref to |
| 54 | +/// get a non-const handle and walk down the tree again. |
| 55 | + |
| 56 | +#ifndef CPROVER_UTIL_CODE_OBSERVER_H |
| 57 | +#define CPROVER_UTIL_CODE_OBSERVER_H |
| 58 | + |
| 59 | +#include <unordered_map> |
| 60 | +#include <functional> |
| 61 | +#include <type_traits> |
| 62 | + |
| 63 | +#include "symbol_table.h" |
| 64 | +#include "std_code.h" |
| 65 | + |
| 66 | +/// \brief base class for all code observer implementations |
| 67 | +template <class T> class code_observer_baset |
| 68 | +{ |
| 69 | + static_assert( |
| 70 | + std::is_base_of<codet, T>::value, |
| 71 | + "T is not derived from codet"); |
| 72 | + |
| 73 | +public: |
| 74 | + virtual ~code_observer_baset() = default; |
| 75 | + |
| 76 | + /// registers an observer that is associated with specific codet id. |
| 77 | + /// \param id: id of the codet to be registered |
| 78 | + /// \param f: observer callback that is associated with the given, \p id |
| 79 | + /// \param args: arguments associated with the given callback, \p f, this |
| 80 | + // should include a placeholder object (std::placeholder_1), as the first |
| 81 | + // argument. |
| 82 | + template <class F, class... Args> |
| 83 | + void register_observer(const irep_idt &id, const F &&f, Args &&... args) |
| 84 | + { |
| 85 | + auto observer = std::bind(f, args...); |
| 86 | + code_observers_map.insert({id, observer}); |
| 87 | + } |
| 88 | + |
| 89 | +protected: |
| 90 | + /// notifies (if any) the observer that is registered to receive a callback |
| 91 | + /// for the given codet, \p code |
| 92 | + /// \param code: will be passed to the registered observer that |
| 93 | + /// that has the same associated id |
| 94 | + void notify(T &code) const |
| 95 | + { |
| 96 | + auto it = code_observers_map.find(code.get_statement()); |
| 97 | + if(it != code_observers_map.end()) |
| 98 | + it->second(code); |
| 99 | + } |
| 100 | + |
| 101 | +private: |
| 102 | + using observerst = std::function<void(T &)>; |
| 103 | + using code_observer_mapt = |
| 104 | + std::unordered_map<const irep_idt, const observerst, irep_id_hash>; |
| 105 | + code_observer_mapt code_observers_map; |
| 106 | +}; |
| 107 | + |
| 108 | +/// \brief codet observer, used to get a callback for every |
| 109 | +/// codet of a specfic type from a given symbol-table. |
| 110 | +class const_code_observert final : private const_expr_visitort, |
| 111 | + public code_observer_baset<const codet> |
| 112 | +{ |
| 113 | +public: |
| 114 | + /// iterates through a given symbol table and for every symbol calls its |
| 115 | + /// associated expression visitor |
| 116 | + /// \param symbol_table: symbol table to visit |
| 117 | + void visit_symbols(const symbol_tablet &symbol_table) |
| 118 | + { |
| 119 | + forall_symbols(it, symbol_table.symbols) |
| 120 | + { |
| 121 | + it->second.value.visit(*this); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + /// Called by expr_vistort, for every expression found. Expressions that are |
| 126 | + /// not of type ID_code are ignored. Otherwise, this function will notify |
| 127 | + /// the observer (if any) that is associated with this code type. |
| 128 | + /// \param expr: expression |
| 129 | + void operator()(const exprt &expr) override |
| 130 | + { |
| 131 | + if(expr.id() == ID_code) |
| 132 | + notify(to_code(expr)); |
| 133 | + } |
| 134 | +}; |
| 135 | + |
| 136 | +#endif // CPROVER_UTIL_CODE_OBSERVER_H |
0 commit comments