2016-01-11 18 views
5

In genere lo scopo di un adattatore è di effettuare chiamate di funzione in un formato modificato. C'è un modo per fare lo stesso genere di cose per le variabili membro? Vale a dire che ho un oggetto che contiene un SomePoint e un altro oggetto che contiene uno DifferentPoint. SomePoint memorizza i suoi dati come variabili membro in maiuscolo X e Y dove AnotherPoint memorizza i suoi dati come variabili membro x e x. Quindi il problema è che non è possibile scrivere una funzione che accetta uno SomePoint o uno DifferentPoint perché non è possibile accedere a o .X (anche utilizzando modelli senza specializzarsi interamente per ogni tipo di punto diverso, nel qual caso si potrebbe anche solo sovraccarico sul tipo di punto)."Adattatore" per variabili membro

La domanda è c'è un modo per creare un adattatore che produrrà .X per un SomePoint quando è richiesto ? Entrambi questi tipi di punti sono classi di libreria, quindi non posso modificare l'interno di nessuno dei due direttamente. Vorrei anche evitare di copiare i dati.

+0

Sono confuso su come questo problema sarebbe fondamentalmente diverso se si trattasse di funzioni membro 'getx()' e 'getX()'. –

+0

@ChristianHackl Si potrebbe semplicemente modificare l'implementazione della funzione in un adattatore per restituire il valore corretto (si potrebbe avere un adattatore per classe Point). –

+0

Perché non scrivere tale funzione dell'adattatore per la variabile membro, quindi? Qualcosa come 'int getX (SomePoint const &)' e 'int getX (DifferentPoint const &)'. –

risposta

9

Il modo normale per farlo è scrivere una classe di caratteri per specificare come estrarre i dati desiderati.

Ecco una possibile implementazione utilizzando pointer-to-members. Potresti trasformarli in funzioni o lambda se preferisci.

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

Quindi si usa in questo modo:

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

Scrivi una classe adattatore Point che ha sintassi conversione implicita per entrambi i tipi di destinazione. Nota richiede che i dati vengono copiati, quindi non è l'ideale:

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

Non è l'ideale, ma se non è possibile accedere le parti interne delle altre due classi, allora questa è una possibile soluzione. Naturalmente ho pensato che il vostro X e Y erano contemporaneamente x e y ...

Usa allora è

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

soluzione TartanLlama sopra è più flessibile, però, che consente vari tipi di X e Y.

+1

Copia anche tutti i dati. Non l'ho menzionato nella domanda (modifico) ma voglio evitare di copiare tutti i dati. –

+0

Siamo spiacenti. Aggiungerò un piccolo avvertimento in alto per indicare che questo requisito non è soddisfatto nella mia risposta. – Dennis

0

Personalmente preferisco ereditare pubblicamente da uno dei "criminali" e introdurre un riferimento al membro con un nome diverso. Credo che sia meno tipografico e più conveniente da usare rispetto all'adattatore e ai tratti menzionati nelle altre risposte.

+0

Questa non è un'opzione quando si utilizzano classi già create, ad esempio classi di libreria. –

+1

@ EdwinRodríguez, perché? Non vedo alcun problema con questo. – SergeyA

+0

Mi dispiace aver frainteso quello che hai detto –

1

Sulla base di ciò che ha detto TartanLlama, è possibile utilizzare una funzione gratuita simile a std::tuple e ottenere <>.

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