Conversation
Added: - `transport::ISession` - `transport::IMessage[Rx|Ts]Session` - `transport::I[Request|Response][Rx|Ts]Session` Also: - add "std: [14, 17, 20]" to the build matrix - add hash tag triggering by #verification #docs tags - strip repo absolute path prefix from doxygen file paths #docs --------- Co-authored-by: Sergei Shirokov <[email protected]>
- Added `ITransport::makeMessage[Rx|Tx]Session` methods - Added `ITransport::make[Request|Response][Rx|Tx]Session` methods - Added `Priority`, `[Service]TransferMetadata`, `PayloadFragments`, `MessageRxTransfre` and `ServiceRxTransfer` types. - Added`transport::AnyError` - Added implementation of `DynamicBuffer`. Also: - fixed "Coverage" build flavor, and artifacts upload - added `libcyphal::UniquePtr` & `libcyphal::Expected` - fixed "rule of five" deficiency
- Added `ITransport`, `ICanTransport` & `IUpdTransport` - `PayloadFragment` now immutable; `FragmentBuffer` is mutable span.
- `canardInit` is in use. - CAN transport memory resource is connected to the canard allocate/free. - Implemented `getLocalNodeId` Also: - Get rid of extra `Factory` entities.
- `cetl::VariableLengthArray` is now in use to store CAN transport media interfaces. - min MTU from all media is reported by the `ICanTransport::getProtocolParams`. - added corresponding unit tests (to cover `getProtocolParams`). Also: - a bit reworked (simplified) canard memory alloc/free
thirtytwobits
left a comment
There was a problem hiding this comment.
There's a complete lack of interesting ASCII art in this source so far. Very boring!
| CETL_NODISCARD virtual cetl::optional<MessageRxTransfer> receive() = 0; | ||
| }; | ||
|
|
||
| class IMessageTxSession : public IRunnable |
There was a problem hiding this comment.
same comment here as the previous review and for all types in this PR: please start with interface and class documentation. This provides us with a mini-design review as we can state the contract without having the implementation written yet.
There was a problem hiding this comment.
This is sensible but:
-
Sometimes it is necessary to provide more holistic documentation covering multiple entities at once instead of scattering it per entity. This is one such case. We could copy-paste relevant parts of the design doc to the
ITransportdocs intransport.hppand put references to that in the class comments here. The relevant sections are:- https://github.com/OpenCyphal-Garage/libcyphal/blob/main/docs/design/readme.md#session-factory-methods
- https://github.com/OpenCyphal-Garage/libcyphal/blob/main/docs/design/readme.md#session-data-flow
-
Some methods do not require documentation. If you have
Thing getThing() const noexcept, a doc comment saying "Gets the thing" adds nothing but noise. I would prefer to keep obvious cases undocumented.
There was a problem hiding this comment.
As, I wrote for other PR, documentation for public stuff is definitely coming, but I believe we first need some more or less stable api agreed upon. Design doc is very good and very insightful, but at the same time sometimes leaves some (devil in) details unspecified, which might affect result interfaces/implementation quite significantly (like we already discussing about error handling strategies).
There was a problem hiding this comment.
It's mostly the class-level documentation that I'm struggling with. I'm left to linking your intentions in these PRs with a single word – the class name – against the design document. If you have to c&p from that document it's fine just help me out with some context when you first define a class, interface, or data structure.
1. Less `auto` according AUTOSAR A7-1-5 2. Less `struct` according AUTOSAR A11-0-2 3. C.35: A base class destructor should be either public and virtual, or protected and non-virtual. 4. `IMedia::setFilters` now fallible.
|
|
||
| protected: | ||
| IRunnable() = default; | ||
| virtual ~IRunnable() = default; |
There was a problem hiding this comment.
It's sorta a nitpick but per Core Cpp Guidelines C35 the ~IRunnable() shouldn't be virtual. Can we establish this as a standard in this codebase too?
There was a problem hiding this comment.
It's a bit tricky (for me at least) to decide when we want destructor accessible and when we don't... Even for interfaces we currently do move them into unique_ptr<Concrete> → unique_ptr<Interface> which will require them public.
There was a problem hiding this comment.
We shouldn't manage the lifecycle of an object through it's interface. This can lead to dangerous assumptions and amputations (https://godbolt.org/z/1Tj4nzehd). This is what the , voldemort_ptr, dark-ptrinterface_ptr type is meant to solve.
There was a problem hiding this comment.
Thank You Scott for very representable example (with legless dog). Please see latest commit where I moved destructors for "external" entities (IMedia-s and IMultiplexer) to protected area. BUT, I can't currently do the same for IRunnable up to various IXxxSession-s we return to user as unique ptrs. This is b/c of our current pmr implementation of UniquePtr - it REQUIRES currently that ~Interface will be available for deleter. The only way I see to remove such requirement is to have type-erased deleter which will capture in a closure original concrete type allocator. It will require cetl::function (not yet available). IMHO it has to be type-erased - otherwise we can't have proper UniquePtr<IX> → UniquePtr<IY> conversions (assuming of course that X and Y convertible). I discussed this with Pavel - it seems that he agrees with me.
BTW, such type-erased deleter should also solve current standing issue OpenCyphal/CETL#118 (see 2nd bullet)
There was a problem hiding this comment.
Okay. Let's track this with TODOs in the code. Perhaps I'll take on this work in CETL. It'll give me a reason to move my godbolt example into github.
| IMedia(const IMedia&) = delete; | ||
| IMedia& operator=(IMedia&&) = delete; | ||
| IMedia& operator=(const IMedia&) = delete; | ||
| virtual ~IMedia() = default; |
There was a problem hiding this comment.
protected/non-virtual
thirtytwobits
left a comment
There was a problem hiding this comment.
Generally all good here. Please tighten up the interface destructors as this can be hard to fix as the design evolved.
The biggest issue is the open issue on media-layer memory ownership. If we need a interim design call I can do that.
| /// Copyright Amazon.com Inc. or its affiliates. | ||
| /// SPDX-License-Identifier: MIT | ||
|
|
||
| #ifndef LIBCYPHAL_TRANSPORT_DEFINES_HPP_INCLUDED |
There was a problem hiding this comment.
can we call this types.hpp instead? We specifically do not want #defines
There was a problem hiding this comment.
I will rename it in the next (347) pr.
| IMultiplexer(const IMultiplexer&) = delete; | ||
| IMultiplexer& operator=(IMultiplexer&&) = delete; | ||
| IMultiplexer& operator=(const IMultiplexer&) = delete; | ||
| virtual ~IMultiplexer() = default; |
There was a problem hiding this comment.
Same here. Interface destructors should always be protected/non-virtual
There was a problem hiding this comment.
I get it for external entity interfaces (like media and multiplexer), but for our own entities we implement (and return them via makeXxx as unique ptrs) it still blurry which pattern to use.
There was a problem hiding this comment.
Internal implementations would not be destructed via the interface though? They would use their complete internal types.
|
|
||
| // MARK: IRunnable | ||
|
|
||
| void run(const TimePoint) override {} |
There was a problem hiding this comment.
prefer final to override. If actually using override document how subclasses must handle the base-class implementation (e.g. they shall-or-shall-not invoke it. They shall-invoke it first/last. etc).
There was a problem hiding this comment.
At class level we have final - afaik it means that all the virtual methods are final as well. do you want make it even more explicit?
There was a problem hiding this comment.
About "shall-or-shall-not invoke" I can't comment yet (due to lack of fine understanding of requirements), but I agree we should have (eventually) such documentation.
There was a problem hiding this comment.
Eh. I guess if the class is final the sure. It's fine.
thirtytwobits
left a comment
There was a problem hiding this comment.
As discussed on call: we have the right issues captured now to ensure my overarching concerns will be addressed moving forward
| Interface() = default; | ||
| Interface(Interface&&) noexcept = default; | ||
| Interface& operator=(Interface&&) noexcept = default; | ||
|
|
There was a problem hiding this comment.
the rule of 5 requires the dtor to be explicitly defaulted
There was a problem hiding this comment.
will do it at after-next pr
First draft of transport interfaces
Added:
transport::ISessiontransport::IMessage[Rx|Ts]Sessiontransport::I[Request|Response][Rx|Ts]SessionAlso: