Skip to content
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

feat!: add support for generic outputs #141

Open
wants to merge 12 commits into
base: feat/generic_pubsub_dev
Choose a base branch
from

Conversation

bpapaspyros
Copy link
Member

@bpapaspyros bpapaspyros commented Sep 26, 2024

Description

The motivation for this PR is to provide the necessary "plumbing" that allows for adding generic (but supported by ROS) output types.

This PR solves the issue by:

  • moving to C++20 as our main C++ standard
  • introducing C++20 concepts to allow for compile-time decisions regarding the message and data types
  • restructuring the PublisherHandler and PublisherInterface
  • introducing compile-time condition logic to re-route the the execution logic according to message/data types, including (but not limited):
    • PublisherHandler and PublisherInterface
    • MessagePair and MessagePairInterface

Review guidelines

Estimated Time of Review: 20 minutes

Checklist before merging:

  • Confirm that the relevant changelog(s) are up-to-date in case of any user-facing changes

@bpapaspyros bpapaspyros self-assigned this Sep 26, 2024
@bpapaspyros bpapaspyros force-pushed the feat/generic_msgs branch 2 times, most recently from a7eaf5f to 448a862 Compare September 27, 2024 12:37
Copy link
Member

@domire8 domire8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice! I'm curious if we could

  • make this PR target a different staging branch
  • only change C++ version, and modulo core without changing anything in modulo_components

If we can do that (basically enable generic publishing and message pairs without changing anything in the interface) then we should go for it and close this very first stage

source/modulo_components/package.xml Outdated Show resolved Hide resolved
@bpapaspyros bpapaspyros changed the title feat!: add support for generic inputs/outputs feat!: add support for generic outputs Sep 29, 2024
#include <state_representation/space/cartesian/CartesianState.hpp>
#include <state_representation/space/joint/JointState.hpp>

namespace modulo_core::concepts {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domire8 @eeberhard We should agree on concept names/naming convention before merging as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really exposed to the user and derived components right? Just to know the importance of naming

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is included in Component.hpp so it is exposed and can be used in that respect, yes. But it is not something the user needs to interact with to use the library (they will not have to provide them anywhere).

@domire8
Copy link
Member

domire8 commented Sep 29, 2024

Please target a copy of main branch here, not main directly. Will be better to stage everything and fast forward to main when we have the complete feature in cpp and python ready and tested

@bpapaspyros bpapaspyros changed the base branch from main to feat/generic_pubsub_dev September 29, 2024 20:31
Copy link
Member

@domire8 domire8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, some minor comments

@@ -52,6 +64,48 @@ class MessagePair : public MessagePairInterface {
*/
void set_data(const std::shared_ptr<DataT>& data);

protected:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be private?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes yes, you are right.

* @throws modulo_core::exceptions::NullPointerException if the publisher pointer is null
*/
void on_activate();
* @brief Activate ROS publisher.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked the previous docstrings better, or we just do @copydoc from the parent class

PublisherType type_; ///< The type of the publisher interface
PublisherType type_;///< The type of the publisher interface

protected:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have public, protected, private in that order please

if (this->message_pair_ == nullptr) {
throw exceptions::NullPointerException("Message pair is not set, cannot deduce message type");
}
if (this->message_pair_->get_type() == MessageType::CUSTOM_MESSAGE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this block, why do we need it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, indeed this is a bit hacky, but I don't have a cleaner solution yet. We can give it a moment of thought later on in this PR or I can leave a TODO to refactor.

  • What it does is:

The problem starts from the fact that for generic types we need to have MsgT or explicitly know it. This is ok for translated types, hence PublisherInterface::publish can be used (else block).

For generic types we need to

  1. override the publish in PublisherHandler otherwise we can't deduce MsgT
  2. and then explicitly call publish(MsgT&) which is compatible for all types
  • Why the constexpr block

Since both generic and translated types will go through this publish function, it means that the compiler needs to resolve write<MsgT, MsgT> for all MessageTypes even though there is a runtime condition for MessageType::CUSTOM_MESSAGE.

The solution is to introduce a secondary compile-time condition to hide this block when translated types are used.

I currently don't have a better alternative to this.

|| std::same_as<DataT, std_msgs::msg::Float64> || std::same_as<DataT, std_msgs::msg::Float64MultiArray>
|| std::same_as<DataT, std_msgs::msg::Int32> || std::same_as<DataT, std_msgs::msg::String>;

template<typename DataT>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the TranslatedOrEncodedDataT and NonTranslatedDataT will be required for the integration in the components?

Also, why not add || std::same_as<DataT, EncodedState> directly to TranslatedDataT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will now even go as far as saying that the unused concepts could be removed from this PR such that it doesn't go out of scope

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do need to type check separately, but I do agree that this is a bit out of scope with the new form of this PR. I will remove it now and then we can discuss again in the corresponding PR

|| std::same_as<DataT, bool> || std::same_as<DataT, std::string> || std::same_as<DataT, std::vector<double>>;

template<typename DataT>
concept CoreDataT = StateRepresentationDataT<DataT> || CorePrimitivesT<DataT>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
concept CoreDataT = StateRepresentationDataT<DataT> || CorePrimitivesT<DataT>;
concept CoreDataT = std::derived_from<DataT, state_representation::State> || CorePrimitivesT<DataT>;

Could we get rid of the StateRepresentationDataT like that or will it be required somewhere else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is honestly never used directly (apart from what you see). So yes, we can cut down. I think NonTranslatedDataT is also the same case. I'll clean this up


// Translation concepts

template<typename DataT>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah also, now I get what was confusing me a bit here. In the translation concepts we should use MsgT instead of DataT and TranslatedMsgT instead of TranslatedDataT (and so on) no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am actually not sure here. I use them to both type check DataT and MsgT. Perhaps we should drop Data/Msg altogether and stay generic by saying TranslatedTypeT and so on. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants