Skip to content

Commit 2497fdc

Browse files
committed
Verbose error messages in case of missing includes
1 parent 889b255 commit 2497fdc

File tree

2 files changed

+118
-21
lines changed

2 files changed

+118
-21
lines changed

test_tf2/test/test_convert.cpp

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,65 @@
4141
#include <tf2_eigen/tf2_eigen.h>
4242
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
4343

44+
#include <geometry_msgs/msg/vector3_stamped.hpp>
45+
46+
47+
// test of tf2 type traits
48+
static_assert(
49+
!tf2::impl::MessageHasStdHeader<geometry_msgs::msg::Vector3>::value,
50+
"MessageHasStdHeader traits error");
51+
static_assert(
52+
tf2::impl::MessageHasStdHeader<geometry_msgs::msg::Vector3Stamped>::value,
53+
"MessageHasStdHeader traits error");
54+
55+
namespace
56+
{
57+
struct MyPODMessage
58+
{
59+
int header;
60+
};
61+
62+
template<typename Alloc>
63+
struct MyAllocMessage
64+
{
65+
int header;
66+
};
67+
68+
static_assert(
69+
!tf2::impl::MessageHasStdHeader<MyPODMessage>::value,
70+
"MessageHasStdHeader traits error");
71+
72+
template<typename T>
73+
struct MyAllocator
74+
{
75+
using value_type = T;
76+
using size_type = unsigned int;
77+
T * allocate(size_type);
78+
void deallocate(T *, size_type);
79+
template<class U>
80+
struct rebind { typedef MyAllocator<U> other; };
81+
};
82+
using MyVec = geometry_msgs::msg::Vector3_<MyAllocator<void>>;
83+
using MyVecStamped = geometry_msgs::msg::Vector3Stamped_<MyAllocator<void>>;
84+
using MyMessage = MyAllocMessage<MyAllocator<void>>;
85+
86+
static_assert(!tf2::impl::MessageHasStdHeader<MyVec>::value, "MessageHasStdHeader traits error");
87+
static_assert(
88+
tf2::impl::MessageHasStdHeader<MyVecStamped>::value,
89+
"MessageHasStdHeader traits error");
90+
91+
static_assert(
92+
!tf2::impl::MessageHasStdHeader<MyMessage>::value,
93+
"MessageHasStdHeader traits error");
94+
}
95+
4496
using Vector6d = Eigen::Matrix<double, 6, 1>;
4597

4698
TEST(tf2Convert, kdlToBullet)
4799
{
48100
double epsilon = 1e-9;
49101

50-
tf2::Stamped<btVector3> b(btVector3(1,2,3), tf2::timeFromSec(0), "my_frame");
102+
tf2::Stamped<btVector3> b(btVector3(1, 2, 3), tf2::timeFromSec(0), "my_frame");
51103

52104
tf2::Stamped<btVector3> b1 = b;
53105
tf2::Stamped<KDL::Vector> k1;
@@ -74,7 +126,7 @@ TEST(tf2Convert, kdlBulletROSConversions)
74126
{
75127
double epsilon = 1e-9;
76128

77-
tf2::Stamped<btVector3> b1(btVector3(1,2,3), tf2::timeFromSec(0), "my_frame"), b2, b3, b4;
129+
tf2::Stamped<btVector3> b1(btVector3(1, 2, 3), tf2::timeFromSec(0), "my_frame"), b2, b3, b4;
78130
geometry_msgs::msg::PointStamped r1, r2, r3;
79131
tf2::Stamped<KDL::Vector> k1, k2, k3;
80132

@@ -101,7 +153,7 @@ TEST(tf2Convert, kdlBulletROSConversions)
101153

102154
TEST(tf2Convert, ConvertTf2Quaternion)
103155
{
104-
tf2::Quaternion tq(1,2,3,4);
156+
tf2::Quaternion tq(1, 2, 3, 4);
105157
Eigen::Quaterniond eq;
106158
tf2::convert(tq, eq);
107159

@@ -117,7 +169,7 @@ TEST(tf2Convert, PointVectorDefaultMessagetype)
117169
// as it can return a Vector3 or a Point for certain datatypes
118170
{
119171
// Bullet
120-
const tf2::Stamped<btVector3> b1{btVector3{1.0, 3.0, 4.0}, tf2::get_now(), "my_frame" };
172+
const tf2::Stamped<btVector3> b1{btVector3{1.0, 3.0, 4.0}, tf2::get_now(), "my_frame"};
121173
const geometry_msgs::msg::PointStamped msg = tf2::toMsg(b1);
122174

123175
EXPECT_EQ(msg.point.x, 1.0);
@@ -162,7 +214,7 @@ TEST(tf2Convert, PointVectorOtherMessagetype)
162214
{
163215
const tf2::Vector3 t1{2.0, 4.0, 5.0};
164216
geometry_msgs::msg::Point msg;
165-
const geometry_msgs::msg::Point& msg2 = tf2::toMsg(t1, msg);
217+
const geometry_msgs::msg::Point & msg2 = tf2::toMsg(t1, msg);
166218

167219
// returned reference is second argument
168220
EXPECT_EQ(&msg2, &msg);
@@ -174,7 +226,7 @@ TEST(tf2Convert, PointVectorOtherMessagetype)
174226
// Eigen
175227
const Eigen::Vector3d e1{2.0, 4.0, 5.0};
176228
geometry_msgs::msg::Vector3 msg;
177-
const geometry_msgs::msg::Vector3& msg2 = tf2::toMsg(e1, msg);
229+
const geometry_msgs::msg::Vector3 & msg2 = tf2::toMsg(e1, msg);
178230

179231
// returned reference is second argument
180232
EXPECT_EQ(&msg2, &msg);
@@ -286,7 +338,7 @@ TEST(TfEigenKdl, TestVector3dVector)
286338
EXPECT_EQ(eigen_v, eigen_v1);
287339
}
288340

289-
int main(int argc, char** argv)
341+
int main(int argc, char ** argv)
290342
{
291343
testing::InitGoogleTest(&argc, argv);
292344
return RUN_ALL_TESTS();

tf2/include/tf2/impl/convert.h

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,49 @@
3232
#define TF2__IMPL__CONVERT_H_
3333

3434
#include <geometry_msgs/msg/quaternion.hpp>
35+
#include <std_msgs/msg/header.hpp>
3536

3637
#include "../time.h"
3738
#include "forward.h"
3839

40+
#include <type_traits>
41+
42+
3943
namespace tf2
4044
{
4145
namespace impl
4246
{
47+
48+
/// Helper to always trigger \c static_assert s
49+
template<typename T>
50+
constexpr bool alwaysFalse = false;
51+
52+
/** \brief Check whether a message is stamped and has a std_msgs::msg::Header member.
53+
*
54+
* It will be indicated with the static member valiable \c value .
55+
* \tparam Message The message to check
56+
*/
57+
template<typename Message, typename = int>
58+
struct MessageHasStdHeader : std::false_type {};
59+
60+
/** \brief Check whether a message is stamped and has a std_msgs::msg::Header member.
61+
*
62+
* It will be indicated with the static member valiable \c value .
63+
* \tparam Message The message to check
64+
*/
65+
template<typename Message>
66+
class MessageHasStdHeader<Message, decltype(&Message::header, 0)>
67+
{
68+
template<typename Alloc>
69+
static std::true_type headerIsStdHeader(std_msgs::msg::Header_<Alloc>);
70+
template<typename = void>
71+
static std::false_type headerIsStdHeader(...);
72+
73+
public:
74+
/// true if Message has a member \c header of type std_msgs::msg::Header.
75+
static constexpr bool value = decltype(headerIsStdHeader(std::declval<Message>().header))::value;
76+
};
77+
4378
/**
4479
* \brief Mapping between Datatypes (like \c Vector3d ) and their default ROS Message types.
4580
*
@@ -52,6 +87,9 @@ namespace impl
5287
template<class Datatype, class>
5388
struct DefaultMessageForDatatype
5489
{
90+
static_assert(
91+
alwaysFalse<Datatype>, "No default message type defined, "
92+
"please check your header file includes!");
5593
// using type = ...;
5694
};
5795

@@ -73,6 +111,9 @@ struct DefaultMessageForDatatype
73111
template<class Datatype, class>
74112
struct DefaultTransformType
75113
{
114+
static_assert(
115+
alwaysFalse<Datatype>, "No default transform type defined, "
116+
"please check your header file includes!");
76117
// using type = ...;
77118
};
78119

@@ -100,6 +141,9 @@ struct DefaultTransformType
100141
template<class Datatype, class Message, class>
101142
struct ConversionImplementation
102143
{
144+
static_assert(
145+
alwaysFalse<Datatype>, "No Conversion Implementation available, "
146+
"please check your header file includes!");
103147
// void toMsg(const Datatype&, Message&);
104148
// void fromMsg(const Message&, Datatype&);
105149
};
@@ -140,6 +184,9 @@ struct ConversionImplementation
140184
template<class StampedMessage>
141185
struct StampedMessageTraits
142186
{
187+
static_assert(
188+
alwaysFalse<StampedMessage>, "No traits for this stamped message type available, "
189+
"please check your header file includes!");
143190
// using UntampedType = ...;
144191
// static UntampedType& accessMessage(StampedMsg &);
145192
// static UntampedType getMessage(StampedMsg const&);
@@ -172,6 +219,9 @@ struct StampedMessageTraits
172219
template<class UnstampedMessage>
173220
struct UnstampedMessageTraits
174221
{
222+
static_assert(
223+
alwaysFalse<UnstampedMessage>, "No traits for this message type available, "
224+
"please check your header file includes!");
175225
// using StampedType = ...;
176226
// using StampedTypeWithCovariance = ...;
177227
};
@@ -364,41 +414,36 @@ inline void Converter<false, false>::convert(const A & a, B & b)
364414
fromMsg<>(toMsg<>(a), b);
365415
}
366416

367-
template<typename T>
368-
using void_t = void;
369-
370417
/**
371418
* \brief Default implementation for extracting timestamps and frame IDs.
372419
*
373420
* Both static member functions are for stamped ROS messages.
374-
* They are SFINAE'd out if T is not a stamped ROS message.
375421
*
376422
* \tparam T Arbitrary datatype
377423
*/
378424
template<typename T, int>
379425
struct StampedAttributesHelper
380426
{
427+
static_assert(
428+
MessageHasStdHeader<T>::value,
429+
"Trying to use default implementation for stamped message, "
430+
"but the datatype does not have a Header member.");
431+
381432
/**\brief Get the timestamp from data
382-
* \param t The data input.
433+
* \param[in] t The data input.
383434
* \return The timestamp associated with the data.
384-
*
385-
* The second parameter is needed to hide the default implementation if T is not a stamped ROS message.
386435
*/
387-
static tf2::TimePoint getTimestamp(
388-
const T & t, void_t<typename StampedMessageTraits<T>::UntampedType> * = nullptr)
436+
static tf2::TimePoint getTimestamp(const T & t)
389437
{
390438
tf2::TimePoint timestamp;
391439
tf2::fromMsg<>(t.header.stamp, timestamp);
392440
return timestamp;
393441
}
394442
/**\brief Get the frame_id from data
395-
* \param t The data input.
443+
* \param[in] t The data input.
396444
* \return The frame_id associated with the data.
397-
*
398-
* The second parameter is needed to hide the default implementation if T is not a stamped ROS message.
399445
*/
400-
static std::string getFrameId(
401-
const T & t, void_t<typename StampedMessageTraits<T>::UntampedType> * = nullptr)
446+
static std::string getFrameId(const T & t)
402447
{
403448
return t.header.frame_id;
404449
}

0 commit comments

Comments
 (0)