Skip to content
Snippets Groups Projects
InversionOfControlAccessor.h 4.6 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;
};

} // namespace ChimeraTK

#endif /* CHIMERATK_INVERSION_OF_CONTROL_ACCESSOR_H */