Reputation: 10273
Typically the purpose of an adapter is to make function calls in a modified format. Is there any way to do the same sort of thing for member variables? That is, say I have an object that contains a SomePoint
and another object that contains a DifferentPoint
. SomePoint
stores it's data as member variables capitalized X
and Y
where AnotherPoint
stores it's data as member variables lowercase x
and x
. So the problem is that you can't write a function that accepts either a SomePoint
or a DifferentPoint
because you can't access .x
or .X
(even using templates without specializing entirely for each different point type, in which case you might as well just overload on the point type).
The question is is there a way to make an adapter that will produce .X
for a SomePoint
when .x
is requested? Both of these point types are library classes, so I can't edit the internals of either one directly. I would also like to avoid copying the data.
Upvotes: 5
Views: 188
Reputation: 3731
Write an adapter class Point
which has implicit conversion syntax for both of the target types. Note it requires that the data is copied, so not ideal:
class Point {
XType x;
YType y;
public:
Point (const SomePoint& orig) : x(orig.X), y(orig.Y){}
Point (const DifferentPoint& orig) : x(orig.x), y(orig.y){}
XType getX(){return x;};
YType getY(){return y;};
}
It's not ideal but if you cannot access the internals of the other two classes then this is a potential solution. Of course I've assumed that your X
and Y
were the same time as x
and y
...
Use then is
void printX (const Point& point) {
std::cout << point.getX();
}
...
SomePoint origin(0,0);
printX(Point{origin});
TartanLlama's solution above is more flexible though, allowing various types of X and Y.
Upvotes: 0
Reputation: 170094
Building on what TartanLlama said, you could use a free function akin to std::tuple
and its get<>.
#include <tuple>
#include <type_traits>
#include <iostream>
struct SomePoint { double x; double y; };
namespace adapter
{
template <typename T>
struct PointTraits;
template <>
struct PointTraits<SomePoint> {
constexpr static auto getters = std::make_tuple(&SomePoint::x, &SomePoint::y);
};
const unsigned X = 0;
const unsigned Y = 1;
template<
unsigned C, class Point,
class Traits = PointTraits<
std::remove_reference_t<std::remove_cv_t<Point>>
>
>
constexpr decltype(auto) get (Point&& p)
{
return std::forward<Point>(p).*(std::get<C>(Traits::getters));
}
}
int main()
{
using namespace adapter;
SomePoint sp {1, 2};
std::cout << get<X>(sp) << '\n'
<< get<Y>(sp) << std::endl;
return 0;
}
Upvotes: 1
Reputation: 62583
I personally prefer to inherit publicly from one of the 'offenders' and introduce a reference to the differently named member. I believe it's less typing and more convebnient to use than both adaptor and traits mentioned in the other answers.
Upvotes: 0
Reputation: 65620
The usual way to do this is to write a traits class to specify how to get out the data you want.
Here's a possible implementation using pointer-to-members. You could make them into functions or lambdas if you would rather.
template <typename T>
struct PointTraits;
template <>
struct PointTraits<SomePoint> {
constexpr static auto getX = &SomePoint::x;
constexpr static auto getY = &SomePoint::y;
};
template <>
struct PointTraits<AnotherPoint> {
constexpr static auto getX = &AnotherPoint::X;
constexpr static auto getY = &AnotherPoint::Y;
};
Then you would use it like this:
template <typename PointT>
void printX (const PointT& point) {
std::cout << point.*PointTraits<T>::getX;
}
Upvotes: 9