Skip to content
Snippets Groups Projects
InversionOfControlAccessor.h 4.55 KiB
Newer Older
/*
* InversionOfControlAccessor.h
*
*  Created on: Sep 28, 2017
*      Author: Martin Hierholzer
*/

#ifndef CHIMERATK_INVERSION_OF_CONTROL_ACCESSOR_H
#define CHIMERATK_INVERSION_OF_CONTROL_ACCESSOR_H

#include <string>

#include <boost/smart_ptr/shared_ptr.hpp>

#include "Module.h"
#include "VariableNetworkNode.h"

namespace ChimeraTK {

  /** Adds features required for inversion of control to an accessor. This is needed for both the ArrayAccessor and
   *  the ScalarAccessor classes, thus it uses a CRTP. */
  template< typename Derived >
  class InversionOfControlAccessor {

    public:

      /** Unregister at its owner when deleting */
      ~InversionOfControlAccessor() {
        if(getOwner() != nullptr) getOwner()->unregisterAccessor(node);
      }

      /** Change meta data (name, unit, description and optionally tags). This function may only be used on
       *  Application-type nodes. If the optional argument tags is omitted, the tags will not be changed. To clear the
       *  tags, an empty set can be passed. */
      void setMetaData(const std::string &name, const std::string &unit, const std::string &description) {
        node.setMetaData(name, unit, completeDescription(getOwner(), description));
      }
      void setMetaData(const std::string &name, const std::string &unit, const std::string &description,
                       const std::unordered_set<std::string> &tags) {
        node.setMetaData(name, unit, completeDescription(getOwner(), description), tags);
      /** Add a tag. Valid names for tags only contain alpha-numeric characters (i.e. no spaces and no special
       *  characters). */
      void addTag(const std::string &tag) {
        node.addTag(tag);
      }
      /** Add multiple tags. Valid names for tags only contain alpha-numeric characters (i.e. no spaces and no special
       *  characters). */
      void addTags(const std::unordered_set<std::string> &tags) {
        for(auto &tag : tags) node.addTag(tag);
      }

      /** Convert into VariableNetworkNode */
      operator VariableNetworkNode() {
        return node;
      }
      operator const VariableNetworkNode() const {
        return node;
      }
      /** Connect with other node */
      VariableNetworkNode operator>>(const VariableNetworkNode &otherNode) {
        return node >> otherNode;
      }

      /** Replace with other accessor */
      void replace(Derived &&other) {
        assert(static_cast<Derived*>(this)->_impl == nullptr && other._impl == nullptr);
        if(getOwner() != nullptr) getOwner()->unregisterAccessor(node);
        node = other.node;    // just copies the pointer, but other will be destroyed right after this move constructor
        other.node = VariableNetworkNode();
        node.setAppAccessorPointer(static_cast<Derived*>(this));
        // Note: the accessor is registered by the VariableNetworkNode, so we don't have to re-register.
      }

      /** Return the owning module */
      EntityOwner* getOwner() const { return node.getOwningModule(); }

  protected:

      /// complete the description with the full description from the owner
      std::string completeDescription(EntityOwner *owner, const std::string &description) {
        auto ownerDescription = owner->getFullDescription();
        if(ownerDescription == "") return description;
        if(description == "") return ownerDescription;
        return ownerDescription + " - " + description;
      }

      InversionOfControlAccessor(Module *owner, const std::string &name, VariableDirection direction, std::string unit,
          size_t nElements, UpdateMode mode, const std::string &description, const std::type_info* valueType,
          const std::unordered_set<std::string> &tags={})
        : node(owner, static_cast<Derived*>(this), name, direction, unit, nElements, mode,
               completeDescription(owner, description), valueType, tags)
      {
        static_assert(std::is_base_of<InversionOfControlAccessor<Derived>, Derived>::value,
                      "InversionOfControlAccessor<> must be used in a curiously recurring template pattern!");
        if(name.find_first_of("/") != std::string::npos) {
          throw ChimeraTK::logic_error("Accessor names must not contain slashes: '"+name+"' in module '"
                                       +owner->getQualifiedName()+"'.");
        }
        owner->registerAccessor(node);
      }

      /** Default constructor creates a dysfunctional accessor (to be assigned with a real accessor later) */
      InversionOfControlAccessor() {}
      VariableNetworkNode node;

  };

}

#endif /* CHIMERATK_INVERSION_OF_CONTROL_ACCESSOR_H */