Mi piacerebbe essere in grado di implementare dati immutabili in C ++. In breve, dato un oggetto C ++ in cui vorrei modificare una variabile membro, invece di modificare quel membro sul posto vorrei ottenere una nuova copia dell'oggetto con il membro modificato.
Per risolvere questo problema, ho scritto una piccola classe template per rappresentare il campo immutabile:
template <typename ParentStruct, typename T>
class ImmutableField {
public:
ParentStruct set(const T& newNalue) const {
ParentStruct dst = *(getParentPointer());
reinterpret_cast<ImmutableField<ParentStruct, T>*>((
reinterpret_cast<uint8_t*>(&dst) + _offsetInParent))->_value = newNalue;
return dst;
}
const T& get() const {
return _value;
}
private:
friend ParentStruct;
ImmutableField(const ParentStruct* parent, const T& init = T()) :
_offsetInParent(reinterpret_cast<const uint8_t*>(this) - reinterpret_cast<const uint8_t*>(parent)),
_value(init) {}
const ParentStruct* getParentPointer() const {
return reinterpret_cast<const ParentStruct*>(
reinterpret_cast<const uint8_t*>(this) - _offsetInParent);
}
T _value;
int64_t _offsetInParent = 0;
};
Come parametro template, prende il tipo della classe a cui appartiene e il tipo di valore che memorizza. Ha due metodi pubblici, get
e set
. Tieni presente che set
è const
e restituisce una ParentStruct
che rappresenta la versione aggiornata di ParentStruct.
Ora posso, ad esempio, usarlo per implementare un numero complesso come questo:
double sqr(double x) {return x*x;}
struct ComplexNumber {
ComplexNumber() : real(this), imag(this) {}
ImmutableField<ComplexNumber, double> real;
ImmutableField<ComplexNumber, double> imag;
double abs() const {
return sqrt(sqr(real.get()) + sqr(imag.get()));
}
};
E alcuni test di base suggeriscono che funzioni:
#define CHECK(X) if (!(X)) {std::cerr << "Check " #X " failed." << std::endl; abort();}
int main() {
ComplexNumber x = ComplexNumber().real.set(3.0).imag.set(4.0);
CHECK(x.abs() == 5.0);
std::cout << "So far, so good!" << std::endl;
ComplexNumber y = x.real.set(12).imag.set(5);
CHECK(y.abs() == 13.0);
std::cout << "It still works!" << std::endl;
return 0;
}
La mia domanda è : c'è un modo migliore per ottenere ciò che sto cercando di fare e c'è una libreria per farlo in un modo più pulito? La classe ImmutableField
sembra estremamente hacky con un sacco di reinterpret_cast
.