Newer
Older
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
Martin Christoph Hierholzer
committed
#include "Module.h"
#include "VariableNetworkNode.h"
Martin Christoph Hierholzer
committed
#include <boost/smart_ptr/shared_ptr.hpp>
Martin Christoph Hierholzer
committed
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();
/** 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);
void setMetaData(const std::string& name, const std::string& unit, const std::string& description,
const std::unordered_set<std::string>& 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);
/** 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);
/** 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);
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 = {});
/** Default constructor creates a dysfunctional accessor (to be assigned with
* a real accessor later) */
InversionOfControlAccessor() = default;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/********************************************************************************************************************/
/********************************************************************************************************************/
template<typename Derived>
InversionOfControlAccessor<Derived>::~InversionOfControlAccessor() {
if(getOwner() != nullptr) getOwner()->unregisterAccessor(node);
}
/********************************************************************************************************************/
template<typename Derived>
void InversionOfControlAccessor<Derived>::setMetaData(
const std::string& name, const std::string& unit, const std::string& description) {
node.setMetaData(name, unit, completeDescription(getOwner(), description));
}
/********************************************************************************************************************/
template<typename Derived>
void InversionOfControlAccessor<Derived>::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);
}
/********************************************************************************************************************/
template<typename Derived>
void InversionOfControlAccessor<Derived>::addTags(const std::unordered_set<std::string>& tags) {
for(auto& tag : tags) node.addTag(tag);
}
/********************************************************************************************************************/
template<typename Derived>
void InversionOfControlAccessor<Derived>::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();
if(node.getType() == NodeType::Application) {
node.setAppAccessorPointer(static_cast<Derived*>(this));
}
else {
assert(node.getType() == NodeType::invalid);
}
// Note: the accessor is registered by the VariableNetworkNode, so we don't
// have to re-register.
}
/********************************************************************************************************************/
template<typename Derived>
std::string InversionOfControlAccessor<Derived>::completeDescription(
EntityOwner* owner, const std::string& description) {
auto ownerDescription = owner->getFullDescription();
if(ownerDescription == "") return description;
if(description == "") return ownerDescription;
return ownerDescription + " - " + description;
}
/********************************************************************************************************************/
template<typename Derived>
InversionOfControlAccessor<Derived>::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);
}
/********************************************************************************************************************/