22#ifndef ESI_TYPED_PORTS_H
23#define ESI_TYPED_PORTS_H
47 using std::runtime_error::runtime_error;
57 t = alias->getInnerType();
72 return {(bv->getWidth() + 7) / 8, bv->getWidth()};
80 if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
82 std::vector<uint8_t> buf(wi.
bytes, 0);
83 std::memcpy(buf.data(), &data, std::min(wi.
bytes,
sizeof(T)));
87 return MessageData(
reinterpret_cast<const uint8_t *
>(&data),
sizeof(T));
96 if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
101 std::memcpy(&val, msg.
getBytes(), std::min(wi.
bytes,
sizeof(T)));
103 if constexpr (std::is_signed_v<T>) {
105 size_t signByte = signBit / 8;
106 uint8_t signMask = uint8_t(1) << (signBit % 8);
107 if (signByte < wi.
bytes && (msg.
getBytes()[signByte] & signMask)) {
110 val |=
static_cast<T
>(~T(0)) << wi.
bitWidth;
123template <
typename T,
typename =
void>
128 : std::is_convertible<decltype(T::_ESI_ID), std::string_view> {};
148 if constexpr (has_esi_id_v<T>) {
150 if (std::string_view(portType->
getID()) != T::_ESI_ID)
152 "ESI type mismatch: C++ type has _ESI_ID '" +
153 std::string(T::_ESI_ID) +
"' but port type is '" +
155 }
else if constexpr (std::is_void_v<T>) {
156 if (!
dynamic_cast<const VoidType *
>(portType))
158 "void, but port type is '" +
161 }
else if constexpr (std::is_same_v<T, bool>) {
163 auto *bits =
dynamic_cast<const BitsType *
>(portType);
164 if (!bits || bits->getWidth() > 1)
166 "ESI type mismatch: expected BitsType with width <= 1 for "
167 "bool, but port type is '" +
169 }
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
170 auto *sint =
dynamic_cast<const SIntType *
>(portType);
173 "ESI type mismatch: expected SIntType for signed integer, "
174 "but port type is '" +
176 if (sint->getWidth() >
sizeof(T) * 8)
178 "ESI type mismatch: SIntType width " +
179 std::to_string(sint->getWidth()) +
" does not fit in " +
180 std::to_string(
sizeof(T) * 8) +
"-bit signed integer");
182 if (
sizeof(T) > 1 && sint->getWidth() <= (
sizeof(T) / 2) * 8)
184 std::to_string(sint->getWidth()) +
185 " should use a smaller C++ type than " +
186 std::to_string(
sizeof(T) * 8) +
"-bit");
187 }
else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
189 auto *uintPort =
dynamic_cast<const UIntType *
>(portType);
190 auto *bits =
dynamic_cast<const BitsType *
>(portType);
191 if (!uintPort && !bits)
193 "ESI type mismatch: expected UIntType or BitsType for unsigned "
194 "integer, but port type is '" +
196 uint64_t width = uintPort ? uintPort->getWidth() : bits->getWidth();
197 if (width >
sizeof(T) * 8)
199 "ESI type mismatch: bit width " + std::to_string(width) +
200 " does not fit in " + std::to_string(
sizeof(T) * 8) +
201 "-bit unsigned integer");
203 if (
sizeof(T) > 1 && width <= (
sizeof(T) / 2) * 8)
205 std::to_string(width) +
206 " should use a smaller C++ type than " +
207 std::to_string(
sizeof(T) * 8) +
"-bit");
210 std::string(
"Cannot verify type compatibility for C++ type '") +
211 typeid(T).name() +
"' against ESI port type '" +
223template <
typename T,
bool CheckValue = false>
239 if constexpr (CheckValue)
245 if constexpr (CheckValue)
262 static_assert(std::is_integral_v<T>,
263 "Value range checking only supported for integral types");
268 uint64_t width = bvType->
getWidth();
269 if (width >=
sizeof(T) * 8)
271 if constexpr (std::is_signed_v<T>) {
272 int64_t minVal = -(int64_t(1) << (width - 1));
273 int64_t maxVal = (int64_t(1) << (width - 1)) - 1;
274 if (data < minVal || data > maxVal)
276 "Value " + std::to_string(data) +
" out of range for " +
277 std::to_string(width) +
"-bit signed type");
279 uint64_t maxVal = (uint64_t(1) << width) - 1;
280 if (
static_cast<uint64_t
>(data) > maxVal)
282 "Value " + std::to_string(data) +
" out of range for " +
283 std::to_string(width) +
"-bit unsigned type");
343 void connect(std::function<
bool(
const T &)> callback,
351 [cb = std::move(callback), wb](
MessageData data) ->
bool {
352 return cb(fromMessageData<T>(data, wb));
360 return fromMessageData<T>(outData,
wireInfo_);
366 return std::async(std::launch::deferred,
367 [f = std::move(innerFuture), wb]()
mutable -> T {
369 return fromMessageData<T>(data, wb);
405 [cb = std::move(callback)](
MessageData) ->
bool {
return cb(); }, opts);
416 std::launch::deferred,
417 [f = std::move(innerFuture)]()
mutable ->
void { f.get(); });
438template <
typename ArgT,
typename ResultT>
448 "TypedFunction: null Function pointer (getAs failed or wrong type)");
456 std::future<ResultT>
call(
const ArgT &arg) {
459 return std::async(std::launch::deferred,
460 [fut = std::move(f), rwb]()
mutable -> ResultT {
462 return fromMessageData<ResultT>(data, rwb);
476template <
typename ResultT>
485 "TypedFunction: null Function pointer (getAs failed or wrong type)");
495 return std::async(std::launch::deferred,
496 [fut = std::move(f), resType]()
mutable -> ResultT {
498 return fromMessageData<ResultT>(data, resType);
510template <
typename ArgT>
519 "TypedFunction: null Function pointer (getAs failed or wrong type)");
526 std::future<void>
call(
const ArgT &arg) {
528 return std::async(std::launch::deferred,
529 [fut = std::move(f)]()
mutable ->
void { fut.get(); });
550 "TypedFunction: null Function pointer (getAs failed or wrong type)");
559 return std::async(std::launch::deferred,
560 [fut = std::move(f)]()
mutable ->
void { fut.get(); });
578template <
typename ArgT,
typename ResultT>
584 void connect(std::function<ResultT(
const ArgT &)> callback,
585 bool quick =
false) {
588 "TypedCallback: null Callback pointer (getAs failed or wrong type)");
595 ResultT result = cb(fromMessageData<ArgT>(argData, argType));
596 return toMessageData(result, resType);
609template <
typename ResultT>
615 void connect(std::function<ResultT()> callback,
bool quick =
false) {
618 "TypedCallback: null Callback pointer (getAs failed or wrong type)");
624 ResultT result = cb();
638template <
typename ArgT>
644 void connect(std::function<
void(
const ArgT &)> callback,
bool quick =
false) {
647 "TypedCallback: null Callback pointer (getAs failed or wrong type)");
653 cb(fromMessageData<ArgT>(argData, argType));
655 return MessageData(&zero, 1);
674 void connect(std::function<
void()> callback,
bool quick =
false) {
677 "TypedCallback: null Callback pointer (getAs failed or wrong type)");
Bit vectors include signed, unsigned, and signless integers.
uint64_t getWidth() const
Bits are just an array of bits.
const Type * getType() const
A logical chunk of data representing serialized data.
const uint8_t * getBytes() const
const T * as() const
Cast to a type.
size_t getSize() const
Get the size of the data in bytes.
A ChannelPort which reads data from the accelerator.
virtual std::future< MessageData > readAsync()
Asynchronous read.
virtual bool isConnected() const override
virtual void connect(std::function< bool(MessageData)> callback, const ConnectOptions &options={})
virtual void disconnect() override
virtual void read(MessageData &outData)
Specify a buffer to read into.
Type aliases provide a named type which forwards to an inner type.
Root class of the ESI type system.
std::string toString(bool oneLine=false) const
services::CallService::Callback & raw()
void connect(std::function< void(const ArgT &)> callback, bool quick=false)
TypedCallback(services::CallService::Callback *cb)
const services::CallService::Callback & raw() const
services::CallService::Callback * inner
const services::CallService::Callback & raw() const
TypedCallback(services::CallService::Callback *cb)
services::CallService::Callback & raw()
services::CallService::Callback * inner
void connect(std::function< ResultT()> callback, bool quick=false)
const services::CallService::Callback & raw() const
void connect(std::function< void()> callback, bool quick=false)
services::CallService::Callback * inner
TypedCallback(services::CallService::Callback *cb)
services::CallService::Callback & raw()
TypedCallback(services::CallService::Callback *cb)
const services::CallService::Callback & raw() const
services::CallService::Callback * inner
services::CallService::Callback & raw()
void connect(std::function< ResultT(const ArgT &)> callback, bool quick=false)
services::FuncService::Function * inner
TypedFunction(services::FuncService::Function *func)
std::future< void > call(const ArgT &arg)
services::FuncService::Function & raw()
const services::FuncService::Function & raw() const
TypedFunction(services::FuncService::Function *func)
services::FuncService::Function * inner
services::FuncService::Function & raw()
const services::FuncService::Function & raw() const
std::future< ResultT > call()
std::future< void > call()
services::FuncService::Function * inner
services::FuncService::Function & raw()
const services::FuncService::Function & raw() const
TypedFunction(services::FuncService::Function *func)
TypedFunction(services::FuncService::Function *func)
Implicit conversion from Function* (returned by getAs<>()).
std::future< ResultT > call(const ArgT &arg)
services::FuncService::Function & raw()
services::FuncService::Function * inner
const services::FuncService::Function & raw() const
TypedReadPort(ReadChannelPort &port)
void connect(const ChannelPort::ConnectOptions &opts={})
TypedReadPort(ReadChannelPort *port)
std::future< void > readAsync()
const ReadChannelPort & raw() const
void connect(std::function< bool()> callback, const ChannelPort::ConnectOptions &opts={})
TypedReadPort(ReadChannelPort &port)
TypedReadPort(ReadChannelPort *port)
std::future< T > readAsync()
void connect(const ChannelPort::ConnectOptions &opts={})
const ReadChannelPort & raw() const
void connect(std::function< bool(const T &)> callback, const ChannelPort::ConnectOptions &opts={})
void connect(const ChannelPort::ConnectOptions &opts={})
TypedWritePort(WriteChannelPort &port)
TypedWritePort(WriteChannelPort *port)
const WriteChannelPort & raw() const
const WriteChannelPort & raw() const
TypedWritePort(WriteChannelPort &port)
void connect(const ChannelPort::ConnectOptions &opts={})
bool tryWrite(const T &data)
void write(const T &data)
TypedWritePort(WriteChannelPort *port)
void checkValueRange(const T &data)
The "void" type is a special type which can be used to represent no type.
A ChannelPort which sends data to the accelerator.
virtual bool isConnected() const override
virtual void disconnect() override
void write(const MessageData &data)
A very basic blocking write API.
bool flush()
Flush any buffered data.
bool tryWrite(const MessageData &data)
A basic non-blocking write API.
virtual void connect(const ConnectOptions &options={}) override
Set up a connection to the accelerator.
A function call which gets attached to a service port.
const esi::Type * getArgType() const
const esi::Type * getResultType() const
void connect(std::function< MessageData(const MessageData &)> callback, bool quick=false)
Connect a callback to code which will be executed when the accelerator invokes the callback.
A function call which gets attached to a service port.
const esi::Type * getArgType() const
const esi::Type * getResultType() const
std::future< MessageData > call(const MessageData &arg)
void verifyTypeCompatibility(const Type *portType)
MessageData toMessageData(const T &data, WireInfo wi)
Pack a C++ integral value into a MessageData with the given wire byte count.
WireInfo getWireInfo(const Type *portType)
constexpr bool has_esi_id_v
const Type * unwrapTypeAlias(const Type *t)
Unwrap TypeAliasType (possibly recursively) to get the underlying type.
T fromMessageData(const MessageData &msg, WireInfo wi)
Unpack a MessageData into a C++ integral value with the given wire info.
Compute the wire byte count for a port type.