-
Notifications
You must be signed in to change notification settings - Fork 503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue/336 transport iterfaces #343
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
ITransport
docs intransport.hpp
and put references to that in the class comments here. The relevant sections are: -
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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-ptr
interface_ptr
type is meant to solve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
protected/non-virtual
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we call this types.hpp
instead? We specifically do not want #define
s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. Interface destructors should always be protected/non-virtual
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh. I guess if the class is final the sure. It's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the rule of 5 requires the dtor to be explicitly defaulted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do it at after-next pr
First draft of transport interfaces
Added:
transport::ISession
transport::IMessage[Rx|Ts]Session
transport::I[Request|Response][Rx|Ts]Session
Also: