diff options
Diffstat (limited to 'host/lib/include/uhdlib/experts')
-rw-r--r-- | host/lib/include/uhdlib/experts/expert_container.hpp | 352 | ||||
-rw-r--r-- | host/lib/include/uhdlib/experts/expert_factory.hpp | 596 | ||||
-rw-r--r-- | host/lib/include/uhdlib/experts/expert_nodes.hpp | 964 |
3 files changed, 1009 insertions, 903 deletions
diff --git a/host/lib/include/uhdlib/experts/expert_container.hpp b/host/lib/include/uhdlib/experts/expert_container.hpp index da52f6f4a..0beae5504 100644 --- a/host/lib/include/uhdlib/experts/expert_container.hpp +++ b/host/lib/include/uhdlib/experts/expert_container.hpp @@ -8,185 +8,189 @@ #ifndef INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP #define INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP -#include <uhdlib/experts/expert_nodes.hpp> #include <uhd/config.hpp> #include <uhd/utils/noncopyable.hpp> -#include <memory> +#include <uhdlib/experts/expert_nodes.hpp> #include <boost/thread/recursive_mutex.hpp> +#include <memory> namespace uhd { namespace experts { - enum auto_resolve_mode_t { - AUTO_RESOLVE_OFF, - AUTO_RESOLVE_ON_READ, - AUTO_RESOLVE_ON_WRITE, - AUTO_RESOLVE_ON_READ_WRITE - }; - - class UHD_API expert_container : private uhd::noncopyable, public node_retriever_t { - public: //Methods - typedef std::shared_ptr<expert_container> sptr; - - virtual ~expert_container() {}; - - /*! - * Return the name of this container - */ - virtual const std::string& get_name() const = 0; - - /*! - * Resolves all the nodes in this expert graph. - * - * Dependency analysis is performed on the graph and nodes - * are resolved in a topologically sorted order to ensure - * that no nodes receive stale data. - * Nodes and their dependencies are resolved only if they are - * dirty i.e. their contained values have changed since the - * last resolve. - * This call requires an acyclic expert graph. - * - * \param force If true then ignore dirty state and resolve all nodes - * \throws uhd::runtime_error if graph cannot be resolved - */ - virtual void resolve_all(bool force = false) = 0; - - /*! - * Resolves all the nodes that depend on the specified node. - * - * Dependency analysis is performed on the graph and nodes - * are resolved in a topologically sorted order to ensure - * that no nodes receive stale data. - * Nodes and their dependencies are resolved only if they are - * dirty i.e. their contained values have changed since the - * last resolve. - * This call requires an acyclic expert graph. - * - * \param node_name Name of the node to start resolving from - * \throws uhd::lookup_error if node_name not in container - * \throws uhd::runtime_error if graph cannot be resolved - * - */ - virtual void resolve_from(const std::string& node_name) = 0; - - /*! - * Resolves all the specified node and all of its dependencies. - * - * Dependency analysis is performed on the graph and nodes - * are resolved in a topologically sorted order to ensure - * that no nodes receive stale data. - * Nodes and their dependencies are resolved only if they are - * dirty i.e. their contained values have changed since the - * last resolve. - * This call requires an acyclic expert graph. - * - * \param node_name Name of the node to resolve - * \throws uhd::lookup_error if node_name not in container - * \throws uhd::runtime_error if graph cannot be resolved - * - */ - virtual void resolve_to(const std::string& node_name) = 0; - - /*! - * Return a node retriever object for this container - */ - virtual const node_retriever_t& node_retriever() const = 0; - - /*! - * Returns a DOT (graph description language) representation - * of the expert graph. The output has labels for the node - * name, node type (data or worker) and the underlying - * data type for each node. - * - */ - virtual std::string to_dot() const = 0; - - /*! - * Runs several sanity checks on the underlying graph to - * flag dependency issues. Outputs of the checks are - * logged to the console so UHD_EXPERTS_VERBOSE_LOGGING - * must be enabled to see the results - * - */ - virtual void debug_audit() const = 0; - - private: - /*! - * Lookup a node with the specified name in the contained graph - * - * If the node is found, a reference to the node is returned. - * If the node is not found, uhd::lookup_error is thrown - * lookup can return a data or a worker node - * \implements uhd::experts::node_retriever_t - * - * \param name Name of the node to find - * - */ - virtual const dag_vertex_t& lookup(const std::string& name) const = 0; - virtual dag_vertex_t& retrieve(const std::string& name) const = 0; - - /*! - * expert_factory is a friend of expert_container and - * handles all operations that change the structure of - * the underlying dependency graph. - * The expert_container instance owns all data and worker - * nodes and is responsible for release storage on destruction. - * However, the expert_factory allocates storage for the - * node and passes them into the expert_container using the - * following "protected" API calls. - * - */ - friend class expert_factory; - - /*! - * Creates an empty instance of expert_container with the - * specified name. - * - * \param name Name of the container - */ - static sptr make(const std::string& name); - - /*! - * Returns a reference to the resolver mutex. - * - * The resolver mutex guarantees that external operations - * to data-nodes are serialized with resolves of this - * container. - * - */ - virtual boost::recursive_mutex& resolve_mutex() = 0; - - /*! - * Add a data node to the expert graph - * - * \param data_node Pointer to a fully constructed data node object - * \resolve_mode Auto resolve options: Choose from "disabled" and resolve on "read", "write" or "both" - * \throws uhd::runtime_error if node already exists or is of a wrong type (recoverable) - * \throws uhd::assertion_error for other failures (unrecoverable. will clear the graph) - * - */ - virtual void add_data_node(dag_vertex_t* data_node, auto_resolve_mode_t resolve_mode = AUTO_RESOLVE_OFF) = 0; - - /*! - * Add a worker node to the expert graph - * - * \param worker Pointer to a fully constructed worker object - * \throws uhd::runtime_error if worker already exists or is of a wrong type (recoverable) - * \throws uhd::assertion_error for other failures (unrecoverable. will clear the graph) - * - */ - virtual void add_worker(worker_node_t* worker) = 0; - - /*! - * Release all storage for this object. This will delete all contained - * data and worker nodes and remove all dependency relationship and - * resolve callbacks. - * - * The object will be restored to its newly constructed state. Will not - * throw. - */ - virtual void clear() = 0; - }; - -}} +enum auto_resolve_mode_t { + AUTO_RESOLVE_OFF, + AUTO_RESOLVE_ON_READ, + AUTO_RESOLVE_ON_WRITE, + AUTO_RESOLVE_ON_READ_WRITE +}; + +class UHD_API expert_container : private uhd::noncopyable, public node_retriever_t +{ +public: // Methods + typedef std::shared_ptr<expert_container> sptr; + + virtual ~expert_container(){}; + + /*! + * Return the name of this container + */ + virtual const std::string& get_name() const = 0; + + /*! + * Resolves all the nodes in this expert graph. + * + * Dependency analysis is performed on the graph and nodes + * are resolved in a topologically sorted order to ensure + * that no nodes receive stale data. + * Nodes and their dependencies are resolved only if they are + * dirty i.e. their contained values have changed since the + * last resolve. + * This call requires an acyclic expert graph. + * + * \param force If true then ignore dirty state and resolve all nodes + * \throws uhd::runtime_error if graph cannot be resolved + */ + virtual void resolve_all(bool force = false) = 0; + + /*! + * Resolves all the nodes that depend on the specified node. + * + * Dependency analysis is performed on the graph and nodes + * are resolved in a topologically sorted order to ensure + * that no nodes receive stale data. + * Nodes and their dependencies are resolved only if they are + * dirty i.e. their contained values have changed since the + * last resolve. + * This call requires an acyclic expert graph. + * + * \param node_name Name of the node to start resolving from + * \throws uhd::lookup_error if node_name not in container + * \throws uhd::runtime_error if graph cannot be resolved + * + */ + virtual void resolve_from(const std::string& node_name) = 0; + + /*! + * Resolves all the specified node and all of its dependencies. + * + * Dependency analysis is performed on the graph and nodes + * are resolved in a topologically sorted order to ensure + * that no nodes receive stale data. + * Nodes and their dependencies are resolved only if they are + * dirty i.e. their contained values have changed since the + * last resolve. + * This call requires an acyclic expert graph. + * + * \param node_name Name of the node to resolve + * \throws uhd::lookup_error if node_name not in container + * \throws uhd::runtime_error if graph cannot be resolved + * + */ + virtual void resolve_to(const std::string& node_name) = 0; + + /*! + * Return a node retriever object for this container + */ + virtual const node_retriever_t& node_retriever() const = 0; + + /*! + * Returns a DOT (graph description language) representation + * of the expert graph. The output has labels for the node + * name, node type (data or worker) and the underlying + * data type for each node. + * + */ + virtual std::string to_dot() const = 0; + + /*! + * Runs several sanity checks on the underlying graph to + * flag dependency issues. Outputs of the checks are + * logged to the console so UHD_EXPERTS_VERBOSE_LOGGING + * must be enabled to see the results + * + */ + virtual void debug_audit() const = 0; + +private: + /*! + * Lookup a node with the specified name in the contained graph + * + * If the node is found, a reference to the node is returned. + * If the node is not found, uhd::lookup_error is thrown + * lookup can return a data or a worker node + * \implements uhd::experts::node_retriever_t + * + * \param name Name of the node to find + * + */ + virtual const dag_vertex_t& lookup(const std::string& name) const = 0; + virtual dag_vertex_t& retrieve(const std::string& name) const = 0; + + /*! + * expert_factory is a friend of expert_container and + * handles all operations that change the structure of + * the underlying dependency graph. + * The expert_container instance owns all data and worker + * nodes and is responsible for release storage on destruction. + * However, the expert_factory allocates storage for the + * node and passes them into the expert_container using the + * following "protected" API calls. + * + */ + friend class expert_factory; + + /*! + * Creates an empty instance of expert_container with the + * specified name. + * + * \param name Name of the container + */ + static sptr make(const std::string& name); + + /*! + * Returns a reference to the resolver mutex. + * + * The resolver mutex guarantees that external operations + * to data-nodes are serialized with resolves of this + * container. + * + */ + virtual boost::recursive_mutex& resolve_mutex() = 0; + + /*! + * Add a data node to the expert graph + * + * \param data_node Pointer to a fully constructed data node object + * \resolve_mode Auto resolve options: Choose from "disabled" and resolve on "read", + * "write" or "both" \throws uhd::runtime_error if node already exists or is of a + * wrong type (recoverable) \throws uhd::assertion_error for other failures + * (unrecoverable. will clear the graph) + * + */ + virtual void add_data_node( + dag_vertex_t* data_node, auto_resolve_mode_t resolve_mode = AUTO_RESOLVE_OFF) = 0; + + /*! + * Add a worker node to the expert graph + * + * \param worker Pointer to a fully constructed worker object + * \throws uhd::runtime_error if worker already exists or is of a wrong type + * (recoverable) \throws uhd::assertion_error for other failures (unrecoverable. will + * clear the graph) + * + */ + virtual void add_worker(worker_node_t* worker) = 0; + + /*! + * Release all storage for this object. This will delete all contained + * data and worker nodes and remove all dependency relationship and + * resolve callbacks. + * + * The object will be restored to its newly constructed state. Will not + * throw. + */ + virtual void clear() = 0; +}; + +}} // namespace uhd::experts #endif /* INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP */ diff --git a/host/lib/include/uhdlib/experts/expert_factory.hpp b/host/lib/include/uhdlib/experts/expert_factory.hpp index 9e07aa47f..47308d046 100644 --- a/host/lib/include/uhdlib/experts/expert_factory.hpp +++ b/host/lib/include/uhdlib/experts/expert_factory.hpp @@ -18,311 +18,333 @@ namespace uhd { namespace experts { +/*! + * expert_factory is a friend of expert_container and + * handles all operations to create and change the structure of + * the an expert container. + * The expert_factory allocates storage for the nodes in the + * expert_container and passes allocated objects to the container + * using private APIs. The expert_container instance owns all + * data and workernodes and is responsible for releasing their + * storage on destruction. + * + */ +class UHD_API expert_factory : public uhd::noncopyable +{ +public: /*! - * expert_factory is a friend of expert_container and - * handles all operations to create and change the structure of - * the an expert container. - * The expert_factory allocates storage for the nodes in the - * expert_container and passes allocated objects to the container - * using private APIs. The expert_container instance owns all - * data and workernodes and is responsible for releasing their - * storage on destruction. + * Creates an empty instance of expert_container with the + * specified name. * + * \param name Name of the container */ - class UHD_API expert_factory : public uhd::noncopyable { - public: + static expert_container::sptr create_container(const std::string& name); - /*! - * Creates an empty instance of expert_container with the - * specified name. - * - * \param name Name of the container - */ - static expert_container::sptr create_container( - const std::string& name - ); - - /*! - * Add a data node to the expert graph. - * - * \param container A shared pointer to the container to add the node to - * \param name The name of the data node - * \param init_val The initial value of the data node - * \param mode The auto resolve mode - * - * Requirements for data_t - * - Must have a default constructor - * - Must have a copy constructor - * - Must have an assignment operator (=) - * - Must have an equality operator (==) - */ - template<typename data_t> - inline static void add_data_node( - expert_container::sptr container, - const std::string& name, - const data_t& init_val, - const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF - ) { - container->add_data_node(new data_node_t<data_t>(name, init_val), mode); - } + /*! + * Add a data node to the expert graph. + * + * \param container A shared pointer to the container to add the node to + * \param name The name of the data node + * \param init_val The initial value of the data node + * \param mode The auto resolve mode + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + */ + template <typename data_t> + inline static void add_data_node(expert_container::sptr container, + const std::string& name, + const data_t& init_val, + const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF) + { + container->add_data_node(new data_node_t<data_t>(name, init_val), mode); + } - /*! - * Add a expert property to a property tree AND an expert graph - * - * \param container A shared pointer to the expert container to add the node to - * \param subtree A shared pointer to subtree to add the property to - * \param path The path of the property in the subtree - * \param name The name of the data node in the expert graph - * \param init_val The initial value of the data node - * \param mode The auto resolve mode - * - * Requirements for data_t - * - Must have a default constructor - * - Must have a copy constructor - * - Must have an assignment operator (=) - * - Must have an equality operator (==) - */ - template<typename data_t> - inline static property<data_t>& add_prop_node( - expert_container::sptr container, - property_tree::sptr subtree, - const fs_path &path, - const std::string& name, - const data_t& init_val, - const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF - ) { - property<data_t>& prop = subtree->create<data_t>(path, property_tree::MANUAL_COERCE); - data_node_t<data_t>* node_ptr = - new data_node_t<data_t>(name, init_val, &container->resolve_mutex()); - prop.set(init_val); - prop.add_desired_subscriber(std::bind(&data_node_t<data_t>::commit, node_ptr, std::placeholders::_1)); - prop.set_publisher(std::bind(&data_node_t<data_t>::retrieve, node_ptr)); - container->add_data_node(node_ptr, mode); - return prop; - } + /*! + * Add a expert property to a property tree AND an expert graph + * + * \param container A shared pointer to the expert container to add the node to + * \param subtree A shared pointer to subtree to add the property to + * \param path The path of the property in the subtree + * \param name The name of the data node in the expert graph + * \param init_val The initial value of the data node + * \param mode The auto resolve mode + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + */ + template <typename data_t> + inline static property<data_t>& add_prop_node(expert_container::sptr container, + property_tree::sptr subtree, + const fs_path& path, + const std::string& name, + const data_t& init_val, + const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF) + { + property<data_t>& prop = + subtree->create<data_t>(path, property_tree::MANUAL_COERCE); + data_node_t<data_t>* node_ptr = + new data_node_t<data_t>(name, init_val, &container->resolve_mutex()); + prop.set(init_val); + prop.add_desired_subscriber( + std::bind(&data_node_t<data_t>::commit, node_ptr, std::placeholders::_1)); + prop.set_publisher(std::bind(&data_node_t<data_t>::retrieve, node_ptr)); + container->add_data_node(node_ptr, mode); + return prop; + } - /*! - * Add a expert property to a property tree AND an expert graph. - * The property is registered with the path as the identifier for - * both the property subtree and the expert container - * - * \param container A shared pointer to the expert container to add the node to - * \param subtree A shared pointer to subtree to add the property to - * \param path The path of the property in the subtree - * \param init_val The initial value of the data node - * \param mode The auto resolve mode - * - */ - template<typename data_t> - inline static property<data_t>& add_prop_node( - expert_container::sptr container, - property_tree::sptr subtree, - const fs_path &path, - const data_t& init_val, - const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF - ) { - return add_prop_node(container, subtree, path, path, init_val, mode); - } + /*! + * Add a expert property to a property tree AND an expert graph. + * The property is registered with the path as the identifier for + * both the property subtree and the expert container + * + * \param container A shared pointer to the expert container to add the node to + * \param subtree A shared pointer to subtree to add the property to + * \param path The path of the property in the subtree + * \param init_val The initial value of the data node + * \param mode The auto resolve mode + * + */ + template <typename data_t> + inline static property<data_t>& add_prop_node(expert_container::sptr container, + property_tree::sptr subtree, + const fs_path& path, + const data_t& init_val, + const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF) + { + return add_prop_node(container, subtree, path, path, init_val, mode); + } - /*! - * Add a dual expert property to a property tree AND an expert graph. - * A dual property is a desired and coerced value pair - * - * \param container A shared pointer to the expert container to add the node to - * \param subtree A shared pointer to subtree to add the property to - * \param path The path of the property in the subtree - * \param desired_name The name of the desired data node in the expert graph - * \param desired_name The name of the coerced data node in the expert graph - * \param init_val The initial value of both the data nodes - * \param mode The auto resolve mode - * - * Requirements for data_t - * - Must have a default constructor - * - Must have a copy constructor - * - Must have an assignment operator (=) - * - Must have an equality operator (==) - */ - template<typename data_t> - inline static property<data_t>& add_dual_prop_node( - expert_container::sptr container, - property_tree::sptr subtree, - const fs_path &path, - const std::string& desired_name, - const std::string& coerced_name, - const data_t& init_val, - const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF - ) { - bool auto_resolve_desired = (mode==AUTO_RESOLVE_ON_WRITE or mode==AUTO_RESOLVE_ON_READ_WRITE); - bool auto_resolve_coerced = (mode==AUTO_RESOLVE_ON_READ or mode==AUTO_RESOLVE_ON_READ_WRITE); + /*! + * Add a dual expert property to a property tree AND an expert graph. + * A dual property is a desired and coerced value pair + * + * \param container A shared pointer to the expert container to add the node to + * \param subtree A shared pointer to subtree to add the property to + * \param path The path of the property in the subtree + * \param desired_name The name of the desired data node in the expert graph + * \param desired_name The name of the coerced data node in the expert graph + * \param init_val The initial value of both the data nodes + * \param mode The auto resolve mode + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + */ + template <typename data_t> + inline static property<data_t>& add_dual_prop_node(expert_container::sptr container, + property_tree::sptr subtree, + const fs_path& path, + const std::string& desired_name, + const std::string& coerced_name, + const data_t& init_val, + const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF) + { + bool auto_resolve_desired = + (mode == AUTO_RESOLVE_ON_WRITE or mode == AUTO_RESOLVE_ON_READ_WRITE); + bool auto_resolve_coerced = + (mode == AUTO_RESOLVE_ON_READ or mode == AUTO_RESOLVE_ON_READ_WRITE); - property<data_t>& prop = subtree->create<data_t>(path, property_tree::MANUAL_COERCE); - data_node_t<data_t>* desired_node_ptr = - new data_node_t<data_t>(desired_name, init_val, &container->resolve_mutex()); - data_node_t<data_t>* coerced_node_ptr = - new data_node_t<data_t>(coerced_name, init_val, &container->resolve_mutex()); - prop.set(init_val); - prop.set_coerced(init_val); - prop.add_desired_subscriber(std::bind(&data_node_t<data_t>::commit, desired_node_ptr, std::placeholders::_1)); - prop.set_publisher(std::bind(&data_node_t<data_t>::retrieve, coerced_node_ptr)); + property<data_t>& prop = + subtree->create<data_t>(path, property_tree::MANUAL_COERCE); + data_node_t<data_t>* desired_node_ptr = + new data_node_t<data_t>(desired_name, init_val, &container->resolve_mutex()); + data_node_t<data_t>* coerced_node_ptr = + new data_node_t<data_t>(coerced_name, init_val, &container->resolve_mutex()); + prop.set(init_val); + prop.set_coerced(init_val); + prop.add_desired_subscriber(std::bind( + &data_node_t<data_t>::commit, desired_node_ptr, std::placeholders::_1)); + prop.set_publisher(std::bind(&data_node_t<data_t>::retrieve, coerced_node_ptr)); - container->add_data_node(desired_node_ptr, - auto_resolve_desired ? AUTO_RESOLVE_ON_WRITE : AUTO_RESOLVE_OFF); - container->add_data_node(coerced_node_ptr, - auto_resolve_coerced ? AUTO_RESOLVE_ON_READ : AUTO_RESOLVE_OFF); - return prop; - } + container->add_data_node(desired_node_ptr, + auto_resolve_desired ? AUTO_RESOLVE_ON_WRITE : AUTO_RESOLVE_OFF); + container->add_data_node(coerced_node_ptr, + auto_resolve_coerced ? AUTO_RESOLVE_ON_READ : AUTO_RESOLVE_OFF); + return prop; + } - /*! - * Add a dual expert property to a property tree AND an expert graph. - * A dual property is a desired and coerced value pair - * The property is registered with path/desired as the desired node - * name and path/coerced as the coerced node name - * - * \param container A shared pointer to the expert container to add the node to - * \param subtree A shared pointer to subtree to add the property to - * \param path The path of the property in the subtree - * \param init_val The initial value of both the data nodes - * \param mode The auto resolve mode - * - */ - template<typename data_t> - inline static property<data_t>& add_dual_prop_node( - expert_container::sptr container, - property_tree::sptr subtree, - const fs_path &path, - const data_t& init_val, - const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF - ) { - return add_dual_prop_node(container, subtree, path, path + "/desired", path + "/coerced", init_val, mode); - } + /*! + * Add a dual expert property to a property tree AND an expert graph. + * A dual property is a desired and coerced value pair + * The property is registered with path/desired as the desired node + * name and path/coerced as the coerced node name + * + * \param container A shared pointer to the expert container to add the node to + * \param subtree A shared pointer to subtree to add the property to + * \param path The path of the property in the subtree + * \param init_val The initial value of both the data nodes + * \param mode The auto resolve mode + * + */ + template <typename data_t> + inline static property<data_t>& add_dual_prop_node(expert_container::sptr container, + property_tree::sptr subtree, + const fs_path& path, + const data_t& init_val, + const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF) + { + return add_dual_prop_node(container, + subtree, + path, + path + "/desired", + path + "/coerced", + init_val, + mode); + } - /*! - * Add a worker node to the expert graph. - * The expert_container owns and manages storage for the worker - * - * \tparam worker_t Data type of the worker class - * - * \param container A shared pointer to the container to add the node to - * - */ - template<typename worker_t> - inline static void add_worker_node( - expert_container::sptr container - ) { - container->add_worker(new worker_t()); - } + /*! + * Add a worker node to the expert graph. + * The expert_container owns and manages storage for the worker + * + * \tparam worker_t Data type of the worker class + * + * \param container A shared pointer to the container to add the node to + * + */ + template <typename worker_t> + inline static void add_worker_node(expert_container::sptr container) + { + container->add_worker(new worker_t()); + } - /*! - * Add a worker node to the expert graph. - * The expert_container owns and manages storage for the worker - * - * \tparam worker_t Data type of the worker class - * \tparam arg1_t Data type of the first argument to the constructor - * \tparam ... - * \tparam argN_t Data type of the Nth argument to the constructor - * - * \param container A shared pointer to the container to add the node to - * \param arg1 First arg to ctor - * \param ... - * \param argN Nth arg to ctor - * - */ - template<typename worker_t, typename arg1_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1 - ) { - container->add_worker(new worker_t(arg1)); - } + /*! + * Add a worker node to the expert graph. + * The expert_container owns and manages storage for the worker + * + * \tparam worker_t Data type of the worker class + * \tparam arg1_t Data type of the first argument to the constructor + * \tparam ... + * \tparam argN_t Data type of the Nth argument to the constructor + * + * \param container A shared pointer to the container to add the node to + * \param arg1 First arg to ctor + * \param ... + * \param argN Nth arg to ctor + * + */ + template <typename worker_t, typename arg1_t> + inline static void add_worker_node( + expert_container::sptr container, arg1_t const& arg1) + { + container->add_worker(new worker_t(arg1)); + } - template<typename worker_t, typename arg1_t, typename arg2_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2 - ) { - container->add_worker(new worker_t(arg1, arg2)); - } + template <typename worker_t, typename arg1_t, typename arg2_t> + inline static void add_worker_node( + expert_container::sptr container, arg1_t const& arg1, arg2_t const& arg2) + { + container->add_worker(new worker_t(arg1, arg2)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3)); - } + template <typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3) + { + container->add_worker(new worker_t(arg1, arg2, arg3)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3, - arg4_t const & arg4 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3, arg4)); - } + template <typename worker_t, + typename arg1_t, + typename arg2_t, + typename arg3_t, + typename arg4_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3, + arg4_t const& arg4) + { + container->add_worker(new worker_t(arg1, arg2, arg3, arg4)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t, - typename arg5_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3, - arg4_t const & arg4, - arg5_t const & arg5 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5)); - } + template <typename worker_t, + typename arg1_t, + typename arg2_t, + typename arg3_t, + typename arg4_t, + typename arg5_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3, + arg4_t const& arg4, + arg5_t const& arg5) + { + container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t, - typename arg5_t, typename arg6_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3, - arg4_t const & arg4, - arg5_t const & arg5, - arg6_t const & arg6 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6)); - } + template <typename worker_t, + typename arg1_t, + typename arg2_t, + typename arg3_t, + typename arg4_t, + typename arg5_t, + typename arg6_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3, + arg4_t const& arg4, + arg5_t const& arg5, + arg6_t const& arg6) + { + container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t, - typename arg5_t, typename arg6_t, typename arg7_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3, - arg4_t const & arg4, - arg5_t const & arg5, - arg6_t const & arg6, - arg7_t const & arg7 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - } + template <typename worker_t, + typename arg1_t, + typename arg2_t, + typename arg3_t, + typename arg4_t, + typename arg5_t, + typename arg6_t, + typename arg7_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3, + arg4_t const& arg4, + arg5_t const& arg5, + arg6_t const& arg6, + arg7_t const& arg7) + { + container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7)); + } - template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t, - typename arg5_t, typename arg6_t, typename arg7_t, typename arg8_t> - inline static void add_worker_node( - expert_container::sptr container, - arg1_t const & arg1, - arg2_t const & arg2, - arg3_t const & arg3, - arg4_t const & arg4, - arg5_t const & arg5, - arg6_t const & arg6, - arg7_t const & arg7, - arg7_t const & arg8 - ) { - container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - } - }; -}} + template <typename worker_t, + typename arg1_t, + typename arg2_t, + typename arg3_t, + typename arg4_t, + typename arg5_t, + typename arg6_t, + typename arg7_t, + typename arg8_t> + inline static void add_worker_node(expert_container::sptr container, + arg1_t const& arg1, + arg2_t const& arg2, + arg3_t const& arg3, + arg4_t const& arg4, + arg5_t const& arg5, + arg6_t const& arg6, + arg7_t const& arg7, + arg7_t const& arg8) + { + container->add_worker( + new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); + } +}; +}} // namespace uhd::experts #endif /* INCLUDED_UHD_EXPERTS_EXPERT_FACTORY_HPP */ diff --git a/host/lib/include/uhdlib/experts/expert_nodes.hpp b/host/lib/include/uhdlib/experts/expert_nodes.hpp index 665c0e579..b9a0f57bd 100644 --- a/host/lib/include/uhdlib/experts/expert_nodes.hpp +++ b/host/lib/include/uhdlib/experts/expert_nodes.hpp @@ -10,464 +10,544 @@ #include <uhd/config.hpp> #include <uhd/exception.hpp> +#include <uhd/types/time_spec.hpp> #include <uhd/utils/dirty_tracked.hpp> #include <uhd/utils/noncopyable.hpp> -#include <uhd/types/time_spec.hpp> -#include <functional> -#include <boost/thread/recursive_mutex.hpp> -#include <boost/thread.hpp> +#include <stdint.h> #include <boost/core/demangle.hpp> -#include <memory> +#include <boost/thread.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <functional> #include <list> -#include <stdint.h> +#include <memory> namespace uhd { namespace experts { - enum node_class_t { CLASS_WORKER, CLASS_DATA, CLASS_PROPERTY }; - enum node_access_t { ACCESS_READER, ACCESS_WRITER }; - enum node_author_t { AUTHOR_NONE, AUTHOR_USER, AUTHOR_EXPERT }; - - /*!--------------------------------------------------------- - * class dag_vertex_t - * - * This serves as the base class for all nodes in the expert - * graph. Data nodes and workers are derived from this class. - * --------------------------------------------------------- - */ - class dag_vertex_t : private uhd::noncopyable { - public: - typedef std::function<void(std::string)> callback_func_t; - - virtual ~dag_vertex_t() {} - - // Getters for basic info about the node - inline node_class_t get_class() const { - return _class; - } - - inline const std::string& get_name() const { - return _name; - } - - virtual const std::string& get_dtype() const = 0; - - virtual std::string to_string() const = 0; - - // Graph resolution specific - virtual bool is_dirty() const = 0; - virtual void mark_clean() = 0; - virtual void resolve() = 0; - - // External callbacks - virtual void set_write_callback(const callback_func_t& func) = 0; - virtual bool has_write_callback() const = 0; - virtual void clear_write_callback() = 0; - virtual void set_read_callback(const callback_func_t& func) = 0; - virtual bool has_read_callback() const = 0; - virtual void clear_read_callback() = 0; - - protected: - dag_vertex_t(const node_class_t c, const std::string& n): - _class(c), _name(n) {} - - private: - const node_class_t _class; - const std::string _name; - }; - - class data_node_printer { - public: - //Generic implementation - template<typename data_t> - static std::string print(const data_t& val) { - std::ostringstream os; - os << val; - return os.str(); - } - - static std::string print(const uint8_t& val) { - std::ostringstream os; - os << int(val); - return os.str(); - } - - static std::string print(const time_spec_t time) { - std::ostringstream os; - os << time.get_real_secs(); - return os.str(); - } - }; - - /*!--------------------------------------------------------- - * class data_node_t - * - * The data node class hold a passive piece of data in the - * expert graph. A data node is clean if its underlying data - * is clean. Access to the underlying data is provided using - * two methods: - * 1. Special accessor classes (for R/W enforcement) - * 2. External clients (via commit and retrieve). This access - * is protected by the callback mutex. - * - * Requirements for data_t - * - Must have a default constructor - * - Must have a copy constructor - * - Must have an assignment operator (=) - * - Must have an equality operator (==) - * --------------------------------------------------------- - */ - template<typename data_t> - class data_node_t : public dag_vertex_t { - public: - // A data_node_t instance can have a type of CLASS_DATA or CLASS_PROPERTY - // In general a data node is a property if it can be accessed and modified - // from the outside world (of experts) using read and write callbacks. We - // assume that if a callback mutex is passed into the data node that it will - // be accessed from the outside and tag the data node as a PROPERTY. - data_node_t(const std::string& name, boost::recursive_mutex* mutex = NULL) : - dag_vertex_t(mutex?CLASS_PROPERTY:CLASS_DATA, name), _callback_mutex(mutex), _data(), _author(AUTHOR_NONE) {} - - data_node_t(const std::string& name, const data_t& value, boost::recursive_mutex* mutex = NULL) : - dag_vertex_t(mutex?CLASS_PROPERTY:CLASS_DATA, name), _callback_mutex(mutex), _data(value), _author(AUTHOR_NONE) {} - - // Basic info - virtual const std::string& get_dtype() const { - static const std::string dtype( - boost::core::demangle(typeid(data_t).name())); - return dtype; - } - - virtual std::string to_string() const { - return data_node_printer::print(get()); - } - - inline node_author_t get_author() const { - return _author; - } - - // Graph resolution specific - virtual bool is_dirty() const { - return _data.is_dirty(); - } - - virtual void mark_clean() { - _data.mark_clean(); - } - - void resolve() { - //NOP - } - - // Data node specific setters and getters (for the framework) - void set(const data_t& value) { - _data = value; - _author = AUTHOR_EXPERT; - } - - const data_t& get() const { - return _data; - } - - // Data node specific setters and getters (for external entities) - void commit(const data_t& value) { - if (_callback_mutex == NULL) throw uhd::assertion_error("node " + get_name() + " is missing the callback mutex"); - boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex); - set(value); - _author = AUTHOR_USER; - if (is_dirty() and has_write_callback()) { - _wr_callback(std::string(get_name())); //Put the name on the stack before calling - } - } - - const data_t retrieve() const { - if (_callback_mutex == NULL) throw uhd::assertion_error("node " + get_name() + " is missing the callback mutex"); - boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex); - if (has_read_callback()) { - _rd_callback(std::string(get_name())); - } - return get(); - } - - private: - // External callbacks - virtual void set_write_callback(const callback_func_t& func) { - _wr_callback = func; - } - - virtual bool has_write_callback() const { - return bool(_wr_callback); - } - - virtual void clear_write_callback() { - _wr_callback = nullptr; - } - - virtual void set_read_callback(const callback_func_t& func) { - _rd_callback = func; - } - - virtual bool has_read_callback() const { - return bool(_rd_callback); - } - - virtual void clear_read_callback() { - _rd_callback = nullptr; - } - - boost::recursive_mutex* _callback_mutex; - callback_func_t _rd_callback; - callback_func_t _wr_callback; - dirty_tracked<data_t> _data; - node_author_t _author; - }; - - /*!--------------------------------------------------------- - * class node_retriever_t - * - * Node storage is managed by a framework class so we need - * and interface to find and retrieve data nodes to associate - * with accessors. - * --------------------------------------------------------- - */ - class node_retriever_t { - public: - virtual ~node_retriever_t() {} - virtual const dag_vertex_t& lookup(const std::string& name) const = 0; - private: - friend class data_accessor_t; - virtual dag_vertex_t& retrieve(const std::string& name) const = 0; - }; - - /*!--------------------------------------------------------- - * class data_accessor_t - * - * Accessors provide protected access to data nodes and help - * establish dependency relationships. - * --------------------------------------------------------- - */ - class data_accessor_t { - public: - virtual ~data_accessor_t() {} - - virtual bool is_reader() const = 0; - virtual bool is_writer() const = 0; - virtual dag_vertex_t& node() const = 0; - protected: - data_accessor_t(const node_retriever_t& r, const std::string& n): - _vertex(r.retrieve(n)) {} - dag_vertex_t& _vertex; - }; - - template<typename data_t> - class data_accessor_base : public data_accessor_t { - public: - virtual ~data_accessor_base() {} - - virtual bool is_reader() const { - return _access == ACCESS_READER; - } - - virtual bool is_writer() const { - return _access == ACCESS_WRITER; - } - - inline bool is_dirty() const { - return _datanode->is_dirty(); - } - - inline node_class_t get_class() const { - return _datanode->get_class(); - } - - inline node_author_t get_author() const { - return _datanode->get_author(); - } - - protected: - data_accessor_base( - const node_retriever_t& r, const std::string& n, const node_access_t a) : - data_accessor_t(r, n), _datanode(NULL), _access(a) - { - _datanode = dynamic_cast< data_node_t<data_t>* >(&node()); - if (_datanode == NULL) { - throw uhd::type_error("Expected data type for node " + n + - " was " + boost::core::demangle(typeid(data_t).name()) + - " but got " + node().get_dtype()); - } - } - - data_node_t<data_t>* _datanode; - const node_access_t _access; - - private: - virtual dag_vertex_t& node() const { - return _vertex; - } - }; - - /*!--------------------------------------------------------- - * class data_reader_t - * - * Accessor to read the value of a data node and to establish - * a data node => worker node dependency - * --------------------------------------------------------- - */ - template<typename data_t> - class data_reader_t : public data_accessor_base<data_t> { - public: - data_reader_t(const node_retriever_t& retriever, const std::string& node) : - data_accessor_base<data_t>( - retriever, node, ACCESS_READER) {} - - inline const data_t& get() const { - return data_accessor_base<data_t>::_datanode->get(); - } - - inline operator const data_t&() const { - return get(); - } - - inline bool operator==(const data_t& rhs) { - return get() == rhs; - } - - inline bool operator!=(const data_t& rhs) { - return !(get() == rhs); - } - }; - - /*!--------------------------------------------------------- - * class data_reader_t - * - * Accessor to read and write the value of a data node and - * to establish a worker node => data node dependency - * --------------------------------------------------------- - */ - template<typename data_t> - class data_writer_t : public data_accessor_base<data_t> { - public: - data_writer_t(const node_retriever_t& retriever, const std::string& node) : - data_accessor_base<data_t>( - retriever, node, ACCESS_WRITER) {} - - inline const data_t& get() const { - return data_accessor_base<data_t>::_datanode->get(); - } - - inline operator const data_t&() const { - return get(); - } - - inline bool operator==(const data_t& rhs) { - return get() == rhs; - } - - inline bool operator!=(const data_t& rhs) { - return !(get() == rhs); - } - - inline void set(const data_t& value) { - data_accessor_base<data_t>::_datanode->set(value); - } - - inline data_writer_t<data_t>& operator=(const data_t& value) { - set(value); - return *this; - } - - inline data_writer_t<data_t>& operator=(const data_writer_t<data_t>& value) { - set(value.get()); - return *this; - } +enum node_class_t { CLASS_WORKER, CLASS_DATA, CLASS_PROPERTY }; +enum node_access_t { ACCESS_READER, ACCESS_WRITER }; +enum node_author_t { AUTHOR_NONE, AUTHOR_USER, AUTHOR_EXPERT }; + +/*!--------------------------------------------------------- + * class dag_vertex_t + * + * This serves as the base class for all nodes in the expert + * graph. Data nodes and workers are derived from this class. + * --------------------------------------------------------- + */ +class dag_vertex_t : private uhd::noncopyable +{ +public: + typedef std::function<void(std::string)> callback_func_t; + + virtual ~dag_vertex_t() {} + + // Getters for basic info about the node + inline node_class_t get_class() const + { + return _class; + } + + inline const std::string& get_name() const + { + return _name; + } + + virtual const std::string& get_dtype() const = 0; + + virtual std::string to_string() const = 0; + + // Graph resolution specific + virtual bool is_dirty() const = 0; + virtual void mark_clean() = 0; + virtual void resolve() = 0; + + // External callbacks + virtual void set_write_callback(const callback_func_t& func) = 0; + virtual bool has_write_callback() const = 0; + virtual void clear_write_callback() = 0; + virtual void set_read_callback(const callback_func_t& func) = 0; + virtual bool has_read_callback() const = 0; + virtual void clear_read_callback() = 0; + +protected: + dag_vertex_t(const node_class_t c, const std::string& n) : _class(c), _name(n) {} + +private: + const node_class_t _class; + const std::string _name; }; - /*!--------------------------------------------------------- - * class worker_node_t - * - * A node class to implement a function that consumes - * zero or more input data nodes and emits zero or more output - * data nodes. The worker can also operate on other non-expert - * interfaces because worker_node_t is abstract and the client - * is required to implement the "resolve" method in a subclass. - * --------------------------------------------------------- - */ - class worker_node_t : public dag_vertex_t { - public: - worker_node_t(const std::string& name) : - dag_vertex_t(CLASS_WORKER, name) {} - - // Worker node specific - std::list<std::string> get_inputs() const { - std::list<std::string> retval; - for(data_accessor_t* acc: _inputs) { - retval.push_back(acc->node().get_name()); - } - return retval; - } - - std::list<std::string> get_outputs() const { - std::list<std::string> retval; - for(data_accessor_t* acc: _outputs) { - retval.push_back(acc->node().get_name()); - } - return retval; - } - - protected: - // This function is used to bind data accessors - // to this worker. Accessors can be read/write - // and the binding will ensure proper dependency - // handling. - void bind_accessor(data_accessor_t& accessor) { - if (accessor.is_reader()) { - _inputs.push_back(&accessor); - } else if (accessor.is_writer()) { - _outputs.push_back(&accessor); - } else { - throw uhd::assertion_error("Invalid accessor type"); - } - } +class data_node_printer +{ +public: + // Generic implementation + template <typename data_t> + static std::string print(const data_t& val) + { + std::ostringstream os; + os << val; + return os.str(); + } + + static std::string print(const uint8_t& val) + { + std::ostringstream os; + os << int(val); + return os.str(); + } + + static std::string print(const time_spec_t time) + { + std::ostringstream os; + os << time.get_real_secs(); + return os.str(); + } +}; - private: - // Graph resolution specific - virtual bool is_dirty() const { - bool inputs_dirty = false; - for(data_accessor_t* acc: _inputs) { - inputs_dirty |= acc->node().is_dirty(); - } - return inputs_dirty; - } +/*!--------------------------------------------------------- + * class data_node_t + * + * The data node class hold a passive piece of data in the + * expert graph. A data node is clean if its underlying data + * is clean. Access to the underlying data is provided using + * two methods: + * 1. Special accessor classes (for R/W enforcement) + * 2. External clients (via commit and retrieve). This access + * is protected by the callback mutex. + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + * --------------------------------------------------------- + */ +template <typename data_t> +class data_node_t : public dag_vertex_t +{ +public: + // A data_node_t instance can have a type of CLASS_DATA or CLASS_PROPERTY + // In general a data node is a property if it can be accessed and modified + // from the outside world (of experts) using read and write callbacks. We + // assume that if a callback mutex is passed into the data node that it will + // be accessed from the outside and tag the data node as a PROPERTY. + data_node_t(const std::string& name, boost::recursive_mutex* mutex = NULL) + : dag_vertex_t(mutex ? CLASS_PROPERTY : CLASS_DATA, name) + , _callback_mutex(mutex) + , _data() + , _author(AUTHOR_NONE) + { + } + + data_node_t(const std::string& name, + const data_t& value, + boost::recursive_mutex* mutex = NULL) + : dag_vertex_t(mutex ? CLASS_PROPERTY : CLASS_DATA, name) + , _callback_mutex(mutex) + , _data(value) + , _author(AUTHOR_NONE) + { + } + + // Basic info + virtual const std::string& get_dtype() const + { + static const std::string dtype(boost::core::demangle(typeid(data_t).name())); + return dtype; + } + + virtual std::string to_string() const + { + return data_node_printer::print(get()); + } + + inline node_author_t get_author() const + { + return _author; + } + + // Graph resolution specific + virtual bool is_dirty() const + { + return _data.is_dirty(); + } + + virtual void mark_clean() + { + _data.mark_clean(); + } + + void resolve() + { + // NOP + } + + // Data node specific setters and getters (for the framework) + void set(const data_t& value) + { + _data = value; + _author = AUTHOR_EXPERT; + } + + const data_t& get() const + { + return _data; + } + + // Data node specific setters and getters (for external entities) + void commit(const data_t& value) + { + if (_callback_mutex == NULL) + throw uhd::assertion_error( + "node " + get_name() + " is missing the callback mutex"); + boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex); + set(value); + _author = AUTHOR_USER; + if (is_dirty() and has_write_callback()) { + _wr_callback( + std::string(get_name())); // Put the name on the stack before calling + } + } + + const data_t retrieve() const + { + if (_callback_mutex == NULL) + throw uhd::assertion_error( + "node " + get_name() + " is missing the callback mutex"); + boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex); + if (has_read_callback()) { + _rd_callback(std::string(get_name())); + } + return get(); + } + +private: + // External callbacks + virtual void set_write_callback(const callback_func_t& func) + { + _wr_callback = func; + } + + virtual bool has_write_callback() const + { + return bool(_wr_callback); + } + + virtual void clear_write_callback() + { + _wr_callback = nullptr; + } + + virtual void set_read_callback(const callback_func_t& func) + { + _rd_callback = func; + } + + virtual bool has_read_callback() const + { + return bool(_rd_callback); + } + + virtual void clear_read_callback() + { + _rd_callback = nullptr; + } + + boost::recursive_mutex* _callback_mutex; + callback_func_t _rd_callback; + callback_func_t _wr_callback; + dirty_tracked<data_t> _data; + node_author_t _author; +}; - virtual void mark_clean() { - for(data_accessor_t* acc: _inputs) { - acc->node().mark_clean(); - } - } +/*!--------------------------------------------------------- + * class node_retriever_t + * + * Node storage is managed by a framework class so we need + * and interface to find and retrieve data nodes to associate + * with accessors. + * --------------------------------------------------------- + */ +class node_retriever_t +{ +public: + virtual ~node_retriever_t() {} + virtual const dag_vertex_t& lookup(const std::string& name) const = 0; + +private: + friend class data_accessor_t; + virtual dag_vertex_t& retrieve(const std::string& name) const = 0; +}; - virtual void resolve() = 0; +/*!--------------------------------------------------------- + * class data_accessor_t + * + * Accessors provide protected access to data nodes and help + * establish dependency relationships. + * --------------------------------------------------------- + */ +class data_accessor_t +{ +public: + virtual ~data_accessor_t() {} + + virtual bool is_reader() const = 0; + virtual bool is_writer() const = 0; + virtual dag_vertex_t& node() const = 0; + +protected: + data_accessor_t(const node_retriever_t& r, const std::string& n) + : _vertex(r.retrieve(n)) + { + } + dag_vertex_t& _vertex; +}; - // Basic type info - virtual const std::string& get_dtype() const { - static const std::string dtype = "<worker>"; - return dtype; - } +template <typename data_t> +class data_accessor_base : public data_accessor_t +{ +public: + virtual ~data_accessor_base() {} + + virtual bool is_reader() const + { + return _access == ACCESS_READER; + } + + virtual bool is_writer() const + { + return _access == ACCESS_WRITER; + } + + inline bool is_dirty() const + { + return _datanode->is_dirty(); + } + + inline node_class_t get_class() const + { + return _datanode->get_class(); + } + + inline node_author_t get_author() const + { + return _datanode->get_author(); + } + +protected: + data_accessor_base( + const node_retriever_t& r, const std::string& n, const node_access_t a) + : data_accessor_t(r, n), _datanode(NULL), _access(a) + { + _datanode = dynamic_cast<data_node_t<data_t>*>(&node()); + if (_datanode == NULL) { + throw uhd::type_error("Expected data type for node " + n + " was " + + boost::core::demangle(typeid(data_t).name()) + + " but got " + node().get_dtype()); + } + } + + data_node_t<data_t>* _datanode; + const node_access_t _access; + +private: + virtual dag_vertex_t& node() const + { + return _vertex; + } +}; - virtual std::string to_string() const { - return "<worker>"; - } +/*!--------------------------------------------------------- + * class data_reader_t + * + * Accessor to read the value of a data node and to establish + * a data node => worker node dependency + * --------------------------------------------------------- + */ +template <typename data_t> +class data_reader_t : public data_accessor_base<data_t> +{ +public: + data_reader_t(const node_retriever_t& retriever, const std::string& node) + : data_accessor_base<data_t>(retriever, node, ACCESS_READER) + { + } + + inline const data_t& get() const + { + return data_accessor_base<data_t>::_datanode->get(); + } + + inline operator const data_t&() const + { + return get(); + } + + inline bool operator==(const data_t& rhs) + { + return get() == rhs; + } + + inline bool operator!=(const data_t& rhs) + { + return !(get() == rhs); + } +}; - // Workers don't have callbacks so implement stubs - virtual void set_write_callback(const callback_func_t&) {} - virtual bool has_write_callback() const { return false; } - virtual void clear_write_callback() {} - virtual void set_read_callback(const callback_func_t&) {} - virtual bool has_read_callback() const { return false; } - virtual void clear_read_callback() {} +/*!--------------------------------------------------------- + * class data_reader_t + * + * Accessor to read and write the value of a data node and + * to establish a worker node => data node dependency + * --------------------------------------------------------- + */ +template <typename data_t> +class data_writer_t : public data_accessor_base<data_t> +{ +public: + data_writer_t(const node_retriever_t& retriever, const std::string& node) + : data_accessor_base<data_t>(retriever, node, ACCESS_WRITER) + { + } + + inline const data_t& get() const + { + return data_accessor_base<data_t>::_datanode->get(); + } + + inline operator const data_t&() const + { + return get(); + } + + inline bool operator==(const data_t& rhs) + { + return get() == rhs; + } + + inline bool operator!=(const data_t& rhs) + { + return !(get() == rhs); + } + + inline void set(const data_t& value) + { + data_accessor_base<data_t>::_datanode->set(value); + } + + inline data_writer_t<data_t>& operator=(const data_t& value) + { + set(value); + return *this; + } + + inline data_writer_t<data_t>& operator=(const data_writer_t<data_t>& value) + { + set(value.get()); + return *this; + } +}; - std::list<data_accessor_t*> _inputs; - std::list<data_accessor_t*> _outputs; - }; +/*!--------------------------------------------------------- + * class worker_node_t + * + * A node class to implement a function that consumes + * zero or more input data nodes and emits zero or more output + * data nodes. The worker can also operate on other non-expert + * interfaces because worker_node_t is abstract and the client + * is required to implement the "resolve" method in a subclass. + * --------------------------------------------------------- + */ +class worker_node_t : public dag_vertex_t +{ +public: + worker_node_t(const std::string& name) : dag_vertex_t(CLASS_WORKER, name) {} + + // Worker node specific + std::list<std::string> get_inputs() const + { + std::list<std::string> retval; + for (data_accessor_t* acc : _inputs) { + retval.push_back(acc->node().get_name()); + } + return retval; + } + + std::list<std::string> get_outputs() const + { + std::list<std::string> retval; + for (data_accessor_t* acc : _outputs) { + retval.push_back(acc->node().get_name()); + } + return retval; + } + +protected: + // This function is used to bind data accessors + // to this worker. Accessors can be read/write + // and the binding will ensure proper dependency + // handling. + void bind_accessor(data_accessor_t& accessor) + { + if (accessor.is_reader()) { + _inputs.push_back(&accessor); + } else if (accessor.is_writer()) { + _outputs.push_back(&accessor); + } else { + throw uhd::assertion_error("Invalid accessor type"); + } + } + +private: + // Graph resolution specific + virtual bool is_dirty() const + { + bool inputs_dirty = false; + for (data_accessor_t* acc : _inputs) { + inputs_dirty |= acc->node().is_dirty(); + } + return inputs_dirty; + } + + virtual void mark_clean() + { + for (data_accessor_t* acc : _inputs) { + acc->node().mark_clean(); + } + } + + virtual void resolve() = 0; + + // Basic type info + virtual const std::string& get_dtype() const + { + static const std::string dtype = "<worker>"; + return dtype; + } + + virtual std::string to_string() const + { + return "<worker>"; + } + + // Workers don't have callbacks so implement stubs + virtual void set_write_callback(const callback_func_t&) {} + virtual bool has_write_callback() const + { + return false; + } + virtual void clear_write_callback() {} + virtual void set_read_callback(const callback_func_t&) {} + virtual bool has_read_callback() const + { + return false; + } + virtual void clear_read_callback() {} + + std::list<data_accessor_t*> _inputs; + std::list<data_accessor_t*> _outputs; +}; -}} +}} // namespace uhd::experts #endif /* INCLUDED_UHD_EXPERTS_EXPERT_NODE_HPP */ |