2016-01-11 14 views
5

In der Regel dient ein Adapter zum Ausführen von Funktionsaufrufen in einem geänderten Format. Gibt es eine Möglichkeit, die gleiche Art für Member-Variablen zu tun? Das heißt, ich habe ein Objekt, das eine SomePoint und ein anderes Objekt enthält, das eine DifferentPoint enthält. SomePoint speichert seine Daten als Element-Variablen in Großbuchstaben X und Y, wobei AnotherPoint speichert seine Daten als Elementvariablen Kleinbuchstaben x und x. Das Problem ist, dass Sie keine Funktion schreiben können, die entweder SomePoint oder DifferentPoint akzeptiert, weil Sie nicht auf .x oder .X zugreifen können (auch mit Vorlagen ohne Spezialisierung für jeden einzelnen Punkttyp, in diesem Fall können Sie auch nur Überlastung des Punkttyps)."Adapter" für Elementvariablen

Die Frage ist, gibt es eine Möglichkeit, einen Adapter zu machen, der .X für eine SomePoint produziert, wenn .x angefordert wird? Beide Punkttypen sind Bibliotheksklassen, daher kann ich die Interna beider Klassen nicht direkt bearbeiten. Ich möchte auch vermeiden, die Daten zu kopieren.

+0

Ich bin verwirrt, wie dieses Problem grundlegend anders wäre, wenn Sie mit den Memberfunktionen 'getx()' und 'getX()' umgehen würden. –

+0

@ChristianHackl Sie könnten dann einfach die Implementierung der Funktion in einem Adapter ändern, um den korrekten Wert zurückzugeben (Sie könnten einen Adapter pro Point-Klasse haben). –

+0

Warum nicht so eine Adapterfunktion für die Membervariable schreiben, dann? Etwas wie 'int getX (SomePoint const &)' und 'int getX (DifferentPoint const &)'. –

Antwort

9

Der übliche Weg, dies zu tun, ist eine Merkmalsklasse zu schreiben, um festzulegen, wie man die gewünschten Daten erhält.

Hier ist eine mögliche Implementierung mit Zeiger zu Mitgliedern. Sie könnten sie zu Funktionen oder Lambdas machen, wenn Sie möchten.

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; 
}; 

Dann würden Sie es wie folgt verwendet werden:

template <typename PointT> 
void printX (const PointT& point) { 
    std::cout << point.*PointTraits<T>::getX; 
} 
0

schreiben Adapterklasse Point, die für beide der Zieltypen implizite Konvertierung Syntax hat. Hinweis es erfordert, dass die Daten kopiert werden, also nicht 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;}; 
} 

Es ist nicht ideal, aber wenn Sie nicht die Interna der anderen beiden Klassen zugreifen können, dann ist dies eine mögliche Lösung. Natürlich habe ich davon ausgegangen, dass Ihre X und Y zur gleichen Zeit wie x und y ...

Verwenden dann ist

void printX (const Point& point) { 
    std::cout << point.getX(); 
} 
... 
SomePoint origin(0,0); 
printX(Point{origin}); 

TartanLlama Lösung oben waren aber flexibler ist, verschiedene Arten von X ermöglicht und Y.

+1

Dies kopiert auch alle Daten. Ich habe es in der Frage nicht erwähnt (ich werde es bearbeiten), aber ich möchte vermeiden, dass alle Daten kopiert werden. –

+0

Entschuldigung. Ich werde oben eine kleine Einschränkung hinzufügen, um anzuzeigen, dass diese Anforderung in meiner Antwort nicht erfüllt ist. – Dennis

0

Ich persönlich bevorzuge es, öffentlich von einem der "Täter" zu erben und einen Verweis auf das anders benannte Mitglied einzuführen. Ich glaube, es ist weniger tippen und angenehmer zu verwenden als beide Adapter und Eigenschaften in den anderen Antworten erwähnt.

+0

Dies ist keine Option, wenn Sie bereits erstellte Klassen verwenden, z. B. Bibliotheksklassen. –

+1

@ EdwinRodríguez, warum? Ich sehe kein Problem damit. – SergeyA

+0

Entschuldigung, ich habe missverstanden, was du gesagt hast –

1

Aufbauend auf, was TartanLlama sagte, könnten Sie eine freie Funktion verwenden, die std::tuple verwandt ist und seine < erhält.

#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; 
}