Skip to content

Commit

Permalink
refactor to use a std::tuple and be immutable (#1)
Browse files Browse the repository at this point in the history
Signed-off-by: methylDragon <methylDragon@gmail.com>
Co-authored-by: methylDragon <methylDragon@gmail.com>
  • Loading branch information
wjwwood and methylDragon committed Dec 14, 2022
1 parent a15c7e8 commit 73532e1
Show file tree
Hide file tree
Showing 20 changed files with 536 additions and 726 deletions.
62 changes: 62 additions & 0 deletions rclcpp/include/rclcpp/detail/template_contains.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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
{

struct test_template_contains
{
static_assert(template_contains_v<int, int, double>, "failed");
static_assert(template_contains_v<double, int, double>, "failed");
static_assert(template_contains_v<int, int>, "failed");
static_assert(!template_contains_v<int>, "failed");
static_assert(!template_contains_v<int, float>, "failed");
static_assert(!template_contains_v<int, float, double>, "failed");
};

} // namespace

} // namespace detail
} // namespace rclcpp

#endif // RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
63 changes: 63 additions & 0 deletions rclcpp/include/rclcpp/detail/template_unique.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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
{

struct test_template_unique
{
static_assert(template_unique_v<int>, "failed");
static_assert(template_unique_v<int, double>, "failed");
static_assert(!template_unique_v<int, int>, "failed");
static_assert(!template_unique_v<int, double, int>, "failed");
static_assert(!template_unique_v<int, int, double>, "failed");
};

} // namespace

} // namespace detail
} // namespace rclcpp

#endif // RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
17 changes: 0 additions & 17 deletions rclcpp/include/rclcpp/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/node_interfaces/node_interfaces.hpp"
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
Expand Down Expand Up @@ -1455,22 +1454,6 @@ class Node : public std::enable_shared_from_this<Node>
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
get_node_time_source_interface();

/// Return a NodeInterfaces bound with the Node's internal node interfaces.
/**
* Specify which interfaces you want to bind using the template parameters by specifying
* interface support classes to use. Any unmentioned interfaces will be unavailable to bind.
*
* See the rclcpp::node_interfaces::NodeInterfaces class for usage examples and support classes.
*
* \sa rclcpp::node_interfaces::NodeInterfaces
* \param[in] node Node-like object to bind the interfaces of.
* \returns a NodeInterfaces::SharedPtr supporting the stated interfaces, but bound with none
* of them
*/
template<typename ... InterfaceTs>
typename rclcpp::node_interfaces::NodeInterfaces<InterfaceTs...>::SharedPtr
get_node_interfaces();

/// Return the sub-namespace, if this is a sub-node, otherwise an empty string.
/**
* The returned sub-namespace is either the accumulated sub-namespaces which
Expand Down
8 changes: 0 additions & 8 deletions rclcpp/include/rclcpp/node_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/create_timer.hpp"
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
#include "rclcpp/node_interfaces/node_interfaces.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/timer.hpp"
Expand Down Expand Up @@ -394,13 +393,6 @@ Node::get_parameters(
return result;
}

template<typename ... InterfaceTs>
typename rclcpp::node_interfaces::NodeInterfaces<InterfaceTs...>::SharedPtr
Node::get_node_interfaces()
{
return std::make_shared<rclcpp::node_interfaces::NodeInterfaces<InterfaceTs...>>(*this);
}

} // namespace rclcpp

#endif // RCLCPP__NODE_IMPL_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 container 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 container 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_
Loading

0 comments on commit 73532e1

Please sign in to comment.