Simo 0.0.1
Loading...
Searching...
No Matches
Port.h
1/*
2 * Copyright 2026 Matteo Fusi and Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef SIMO_PORT_HH
18#define SIMO_PORT_HH
19
20#include <expected>
21
22#include "Simo/compiler/BoostTypeIndexRuntimeCast.h"
23#include "Simo/compiler/Compiler.h"
24
25namespace Simo {
26
28class SIMO_PUBLIC Port {
29 public:
30 virtual ~Port() = default;
31 BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS()
32
33 using TypeId = boost::typeindex::type_index;
34
36 template <typename Self>
37 TypeId get_type_id(this Self& _) {
38 return boost::typeindex::type_id<Self>();
39 }
40
41 [[nodiscard]]
42 virtual bool connect(Port* other) = 0;
43
44 [[nodiscard]] std::string_view name() const { return name_; }
45 void name(const std::string_view name) { name_ = name; }
46
47 protected:
48 std::string name_;
49};
50
51namespace Ports {
52
53template <typename Payload>
54class InPort;
55
60template <typename Payload>
61class SIMO_PUBLIC OutPort : public Port {
62 public:
63 friend class InPort<Payload>;
64
65 enum struct PORT_STATE : std::uint8_t {
66 EMPTY,
67 FILLED,
68 };
69
70 enum struct SEND_OUTCOME : std::uint8_t {
71 NEW,
72 REPLACED,
73 };
74
75 BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(Port)
76 [[nodiscard]]
77 bool connect(Port* other) override;
78
79 SEND_OUTCOME send(Payload&& payload) {
80 storage = std::move(payload);
81 switch (state_) {
82 case PORT_STATE::EMPTY:
83 state_ = PORT_STATE::FILLED;
84 return SEND_OUTCOME::NEW;
85 case PORT_STATE::FILLED:
86 state_ = PORT_STATE::FILLED;
87 return SEND_OUTCOME::REPLACED;
88 }
89 SIMO_ASSERT(false);
90 }
91
92 void clear() { state_ = PORT_STATE::EMPTY; }
93
94 PORT_STATE state() const { return state_; }
95
96 protected:
97 Payload storage;
98 PORT_STATE state_ = PORT_STATE::EMPTY;
99};
100
105template <typename Payload>
106class SIMO_PUBLIC InPort : public Port {
107 public:
108 friend class OutPort<Payload>;
109 BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(Port)
110 [[nodiscard]]
111 bool connect(Port* other) override;
112
113 Payload&& receive() {
114 SIMO_ASSERT(connecting_port != nullptr);
115 SIMO_ASSERT(connecting_port->state != OutPort<Payload>::PORT_STATE::EMPTY);
116 Payload out_p = std::move(connecting_port->storage);
117 connecting_port->clear();
118 return std::move(out_p);
119 }
120
121 Payload* peek() {
122 if (connecting_port == nullptr ||
123 connecting_port->state() == OutPort<Payload>::PORT_STATE::EMPTY) {
124 return nullptr;
125 }
126 return &connecting_port->storage;
127 }
128
129 void clear() { connecting_port->clear(); }
130
131 protected:
132 OutPort<Payload>* connecting_port = nullptr;
133};
134
135template <typename Payload>
136bool OutPort<Payload>::connect(Port* other) {
137 if (other->get_type_id() != get_type_id<Port>()) {
138 return false;
139 }
140 auto* other_casted = boost::typeindex::runtime_cast<InPort<Payload>*>(other);
141 if (other_casted == nullptr) {
142 return false;
143 }
144 other_casted->connecting_port = this;
145 return true;
146}
147
148template <typename Payload>
149bool InPort<Payload>::connect(Port* other) {
150 if (other->get_type_id() != get_type_id<Port>()) {
151 return false;
152 }
153 auto* other_casted = boost::typeindex::runtime_cast<OutPort<Payload>*>(other);
154 if (other_casted == nullptr) {
155 return false;
156 }
157 connecting_port = other_casted;
158 return true;
159}
160
164template <typename OutPayload, typename InPayload>
165class SIMO_PUBLIC BidirectionalPortTyped : public Port {
166 public:
167 BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(Port)
168 bool connect(Port* other) override;
169
171 OutPort<OutPayload>::SEND_OUTCOME send_out(OutPayload&& payload) {
172 return out_port.send(std::move(payload));
173 }
174
175 void clear_out() { out_port.clear(); }
176
177 void clear_in() { in_port.clear(); }
178
180 InPayload&& receive_in() { return in_port.receive(); }
181
183 InPayload* peek_in() { return in_port.peek(); }
184
185 protected:
186 OutPort<OutPayload> out_port;
187 InPort<InPayload> in_port;
188};
189
190template <typename Payload>
191using BidirectionalPort = BidirectionalPortTyped<Payload, Payload>;
192
193template <typename OutPayload, typename InPayload>
194bool BidirectionalPortTyped<OutPayload, InPayload>::connect(Port* other) {
195 auto* other_casted = boost::typeindex::runtime_cast<
196 BidirectionalPortTyped<InPayload, OutPayload>*>(other);
197 if (other_casted == nullptr) {
198 return false;
199 }
200 return out_port.connect(&other_casted->in_port) &&
201 in_port.connect(&other_casted->out_port);
202}
203
204} // namespace Ports
205
206} // namespace Simo
207
208#endif // SIMO_PORT_HH
Generic port. It offers the virtual method connect.
Definition Port.h:28
TypeId get_type_id(this Self &_)
Utility to the get type of class. Need the type at compile time.
Definition Port.h:37
InPayload && receive_in()
Extract the payload from the in port.
Definition Port.h:180
OutPort< OutPayload >::SEND_OUTCOME send_out(OutPayload &&payload)
Push a payload on the out port.
Definition Port.h:171
InPayload * peek_in()
Peek the payload from the in port.
Definition Port.h:183
Definition Port.h:106
Definition Port.h:61