Skip to content

Commit

Permalink
Implement Unified Node Interface (NodeInterfaces class) (#2041)
Browse files Browse the repository at this point in the history
Co-authored-by: William Woodall <william+github@osrfoundation.org>
Co-authored-by: methylDragon <methylDragon@gmail.com>
  • Loading branch information
methylDragon and wjwwood authored Dec 29, 2022
1 parent c5491a4 commit a73e0bd
Show file tree
Hide file tree
Showing 18 changed files with 811 additions and 0 deletions.
47 changes: 47 additions & 0 deletions rclcpp/include/rclcpp/detail/template_contains.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
#define RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_

#include <type_traits>

namespace rclcpp
{
namespace detail
{

/// Template meta-function that checks if a given T is contained in the list Us.
template<typename T, typename ... Us>
struct template_contains;

template<typename ... Args>
inline constexpr bool template_contains_v = template_contains<Args ...>::value;

template<typename T, typename NextT, typename ... Us>
struct template_contains<T, NextT, Us ...>
{
enum { value = (std::is_same_v<T, NextT>|| template_contains_v<T, Us ...>)};
};

template<typename T>
struct template_contains<T>
{
enum { value = false };
};

} // namespace detail
} // namespace rclcpp

#endif // RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
49 changes: 49 additions & 0 deletions rclcpp/include/rclcpp/detail/template_unique.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
#define RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_

#include <type_traits>

#include "rclcpp/detail/template_contains.hpp"

namespace rclcpp
{
namespace detail
{

/// Template meta-function that checks if a given list Ts contains unique types.
template<typename ... Ts>
struct template_unique;

template<typename ... Args>
inline constexpr bool template_unique_v = template_unique<Args ...>::value;

template<typename NextT, typename ... Ts>
struct template_unique<NextT, Ts ...>
{
enum { value = !template_contains_v<NextT, Ts ...>&& template_unique_v<Ts ...>};
};

template<typename T>
struct template_unique<T>
{
enum { value = true };
};

} // namespace detail
} // namespace rclcpp

#endif // RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
#define RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_

#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>

#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{

// Support and Helper template classes for the NodeInterfaces class.

template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n);

/// Stores the interfaces in a tuple, provides constructors, and getters.
template<typename ... InterfaceTs>
struct NodeInterfacesStorage
{
template<typename NodeT>
NodeInterfacesStorage(NodeT & node) // NOLINT(runtime/explicit)
: interfaces_(init_tuple<decltype(node), InterfaceTs ...>(node))
{}

explicit NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args)
: interfaces_(args ...)
{}

/// Individual Node Interface non-const getter.
template<typename NodeInterfaceT>
std::shared_ptr<NodeInterfaceT>
get()
{
static_assert(
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
"NodeInterfaces class does not contain given NodeInterfaceT");
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
}

/// Individual Node Interface const getter.
template<typename NodeInterfaceT>
std::shared_ptr<const NodeInterfaceT>
get() const
{
static_assert(
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
"NodeInterfaces class does not contain given NodeInterfaceT");
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
}

protected:
std::tuple<std::shared_ptr<InterfaceTs>...> interfaces_;
};

/// Prototype of NodeInterfacesSupports.
/**
* Should read NodeInterfacesSupports<..., T, ...> as "NodeInterfaces supports T", and
* if NodeInterfacesSupport is specialized for T, the is_supported should be
* set to std::true_type, but by default it is std::false_type, which will
* lead to a compiler error when trying to use T with NodeInterfaces.
*/
template<typename StorageClassT, typename ... Ts>
struct NodeInterfacesSupports;

/// Prototype of NodeInterfacesSupportCheck template meta-function.
/**
* This meta-function checks that all the types given are supported,
* throwing a more human-readable error if an unsupported type is used.
*/
template<typename StorageClassT, typename ... InterfaceTs>
struct NodeInterfacesSupportCheck;

/// Iterating specialization that ensures classes are supported and inherited.
template<typename StorageClassT, typename NextInterfaceT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupportCheck<StorageClassT, NextInterfaceT, RemainingInterfaceTs ...>
: public NodeInterfacesSupportCheck<StorageClassT, RemainingInterfaceTs ...>
{
static_assert(
NodeInterfacesSupports<StorageClassT, NextInterfaceT>::is_supported::value,
"given NodeInterfaceT is not supported by rclcpp::node_interfaces::NodeInterfaces");
};

/// Terminating case when there are no more "RemainingInterfaceTs".
template<typename StorageClassT>
struct NodeInterfacesSupportCheck<StorageClassT>
{};

/// Default specialization, needs to be specialized for each supported interface.
template<typename StorageClassT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupports
{
// Specializations need to set this to std::true_type in addition to other interfaces.
using is_supported = std::false_type;
};

/// Terminating specialization of NodeInterfacesSupports.
template<typename StorageClassT>
struct NodeInterfacesSupports<StorageClassT>
: public StorageClassT
{
/// Perfect forwarding constructor to get arguments down to StorageClassT.
template<typename ... ArgsT>
explicit NodeInterfacesSupports(ArgsT && ... args)
: StorageClassT(std::forward<ArgsT>(args) ...)
{}
};

// Helper functions to initialize the tuple in NodeInterfaces.

template<typename StorageClassT, typename ElementT, typename TupleT, typename NodeT>
void
init_element(TupleT & t, NodeT & n)
{
std::get<std::shared_ptr<ElementT>>(t) =
NodeInterfacesSupports<StorageClassT, ElementT>::get_from_node_like(n);
}

template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n)
{
using StorageClassT = NodeInterfacesStorage<Ts ...>;
std::tuple<std::shared_ptr<Ts>...> t;
(init_element<StorageClassT, Ts>(t, n), ...);
return t;
}

/// Macro for creating specializations with less boilerplate.
/**
* You can use this macro to add support for your interface class if:
*
* - The standard getter is get_node_{NodeInterfaceName}_interface(), and
* - the getter returns a non-const shared_ptr<{NodeInterfaceType}>
*
* Examples of using this can be seen in the standard node interface headers
* in rclcpp, e.g. rclcpp/node_interfaces/node_base_interface.hpp has:
*
* RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
*
* If your interface has a non-standard getter, or you want to instrument it or
* something like that, then you'll need to create your own specialization of
* the NodeInterfacesSupports struct without this macro.
*/
#define RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(NodeInterfaceType, NodeInterfaceName) \
namespace rclcpp::node_interfaces::detail { \
template<typename StorageClassT, typename ... RemainingInterfaceTs> \
struct NodeInterfacesSupports< \
StorageClassT, \
NodeInterfaceType, \
RemainingInterfaceTs ...> \
: public NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...> \
{ \
using is_supported = std::true_type; \
\
template<typename NodeT> \
static \
std::shared_ptr<NodeInterfaceType> \
get_from_node_like(NodeT & node_like) \
{ \
return node_like.get_node_ ## NodeInterfaceName ## _interface(); \
} \
\
/* Perfect forwarding constructor to get arguments down to StorageClassT (eventually). */ \
template<typename ... ArgsT> \
explicit NodeInterfacesSupports(ArgsT && ... args) \
: NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...>( \
std::forward<ArgsT>(args) ...) \
{} \
\
std::shared_ptr<NodeInterfaceType> \
get_node_ ## NodeInterfaceName ## _interface() \
{ \
return StorageClassT::template get<NodeInterfaceType>(); \
} \
}; \
} // namespace rclcpp::node_interfaces::detail

} // namespace detail
} // namespace node_interfaces
} // namespace rclcpp

#endif // RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
3 changes: 3 additions & 0 deletions rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "rclcpp/context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
Expand Down Expand Up @@ -177,4 +178,6 @@ class NodeBaseInterface
} // namespace node_interfaces
} // namespace rclcpp

RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)

#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "rclcpp/clock.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
Expand Down Expand Up @@ -50,4 +51,6 @@ class NodeClockInterface
} // namespace node_interfaces
} // namespace rclcpp

RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeClockInterface, clock)

#endif // RCLCPP__NODE_INTERFACES__NODE_CLOCK_INTERFACE_HPP_
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"

Expand Down Expand Up @@ -382,4 +383,6 @@ class NodeGraphInterface
} // namespace node_interfaces
} // namespace rclcpp

RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeGraphInterface, graph)

#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
Loading

0 comments on commit a73e0bd

Please sign in to comment.