I am trying to create a conversion function between objects of two classes (Eigen::Vector3d and MyVector, a Protocol Buffers message), but I want to delay evaluation of the function body until the function is referenced (at which point both classes would be defined).
The function should be callable in files that later define both classes, and it should not cause a compilation error if the function is never used.
I have:
#include <Eigen/Core> // defines Eigen::Vector3d
class MyVector {
public: int set_x(int x) { x_ = x; }
private: int x_;
}
void operator<< (MyVector &msg, const Eigen::Vector3d &vec) {
msg.set_x(vec.x());
}
which I use as:
MyVector msg;
Eigen::Vector3d vec(1, 2, 3);
msg << vec;
This works fine if the function is defined after MyVector, but I would like to be able to define the function such that it can be included in a translation unit that lacks the MyVector class.
I could change the function to this:
template<typename Msg>
void operator<< (Msg &msg, ...
but this is unacceptable because it would apply to other message classes:
quaternion_msg << Eigen::Vector3d(1, 2, 3); // quaternion has xyz but also w!
and I want that to cause a build error.
Is there some kind of template magic that can do that? If not, is there a better way to provide this operator short of adding MyVector to the header file (or its dependencies)?
Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.
Templates in c++ is defined as a blueprint or formula for creating a generic class or a function. To simply put, you can create a single function or single class to work with different data types using templates. C++ template is also known as generic functions or classes which is a very powerful feature in C++.
You can use undefined types or functions in function templates as long as they depend somehow on a template argument and are defined at the point of instantation: non-dependent names are looked up at the pointer where the function template is defined. Dependent names are looked up at the point of instantiation (assuming two-phase name look-up is correctly implemented).
The other side, preventing successful instantiation with other types than a small set of selected types could probably be done using SFINAE:
class MyVector;
template <typename Msg>
typename std::enable_if<std::is_same<Msg, MyVector>::value>::type
operator<< (Msg& msg, Eigen::Vector3d const& vec) {
msg.set_x(vec.x);
}
The type
of std::enable_if<F, T>
is only defined if F
is true
(and T
is defaulted to void
). Since std::is_same<Msg, MyVector>::value
becomes true
only when Msg
is MyVector
this operator only gets defined when instantiated with MyVector
. On the other hand, it gets defined when instantiated a which point MyVector
is hopefully defined.
However, since MyVector
is named in the interface, its name needs to be declared although it doesn't needed to be defined. It is possible to avoid this need if MyVector
could specialize a trait in which case naming MyVector
a type can be delayed until it is defined and the trait is specialized instead. Doing something like this may be important if MyVector
actually happens be a template with defaulted arguments as these can't be forward declared.
Create a small templatized class that is specialized and use that within the templatized operator << function.
ie:
template <typename MSG>
struct Convertor; // generic causes compiler error
template<>
struct Convertor<MyVector> {
static void Convert(MyVector msg, Eigen::vector3d const& vec) {
msg.set_x(vec.x());
}
};
template<typename Msg>
void operator<< (Msg &msg, const Eigen::Vector3d &vec) {
Convertor<Msg>::Convert(msg, vec);
}
You can expand this pattern to more template parameters if needed and specialize for only the conversions you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With