constant member in class - c++

I have this class:
class model
{
private:
link_list list;
float parameter_B1;
float parameter_B0;
public:
model();
float getparameter_B1() const;
float getparameter_B0() const;
float predict();
void info();
};
In which float parameter_B1 and float parameter_B0 are constant , but in order to initialize them , I have to enter constructor body and read a file and using that file's data to find value of these two attributes, but once I set them ,they won't change again.(so I guess they are count as constant)
like this:
model::model()
{
char filename[300];
cout << "enter file name(path\\filname.txt):" << endl;
cin >> filename;
FILE* fp;
fp = fopen(filename, "r+");
float x, y;
if (fp == NULL)
{
cout << "Unable to open the file!" << endl;
exit(EXIT_FAILURE);
}
else
{
while (!feof(fp))
{
if (fscanf(fp, "%f\t%f", &x, &y) == 2)
{
Pair p(x, y);
list.insertNewNode(p);
}
}
}
Pair _average = list.average();
parameter_B1 = list.parameters1(_average);
parameter_B0 = list.parameters2(_average, parameter_B1);
}
but if I change my class to:
class model
{
private:
link_list list;
const float parameter_B1;
const float parameter_B0;
public:
model();
const float getparameter_B1() const;
const float getparameter_B0() const;
float predict();
void info();
};
I will receive these error "model::model()" provides no initialize for:
1. const member "model::parameter_B1"
2. const member "model::parameter_B0"
, but as you can see I can't use initializer list.
what should I do? is not declaring constant variable my only solution?

with delegating constructor, you might do
std::tuple<link_list, float, float> read_model_file()
{
char filename[300];
cout << "enter file name(path\\filname.txt):" << endl;
cin >> filename;
// ...
Pair _average = list.average();
parameter_B1 = list.parameters1(_average);
parameter_B0 = list.parameters2(_average, parameter_B1);
return {std::move(list), parameter_B0, parameter_B1};
}
class model
{
private:
link_list list;
const float parameter_B0;
const float parameter_B1;
public:
model() : model(read_model_file()) {}
model(std::tuple<link_list, float, float> t) :
list(std::get<0>(std::move(t))),
parameter_B0(std::get<1>(std::move(t))),
parameter_B1(std::get<2>(std::move(t)))
{}
// ...
};

Related

Deep copy unsuccessful

might be a stupid question and if it is, let me know, I will delete it as soon as possible. The thing is I have to make a deep copy in class "Kambarys" (ignore mixed languages, I know I shouldn't do that). Program terminates after trying to call function second time. Probably the problem is my syntax in constructor copy, but I can't find the correct one anywhere. One of the requirements is to create langas, durys and kambarys in dynamic memory using "new" and delete windows vector and door in Kambarys destructor. Appreciate the help!
Requirements:
In the main method, use the new operator to create room k1, add windows and doors to it. Write a constructor Room (const Room & k) that would create a correct copy. In the method main, write another room k2. Calculate the length of the baseboards / wall area.
Perform the following steps: k2 = * k1; delete k1;
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
class Langas{
private:
float height;
float widht;
static int countL;
public:
Langas(float h, float w){
this->height=h;
this->widht=w;
countL++;
}
~Langas(){
--countL;
}
float getHeight(){
return height;
}
float getWidht(){
return widht;
}
static int getWindowCount(){
return countL;
}
};
class Durys{
private:
float heightD;
float widhtD;
static int countD;
public:
Durys(float hD, float wD){
this->heightD=hD;
this->widhtD=wD;
countD++;
}
~Durys(){
--countD;
}
float getHeightD(){
return heightD;
}
float getWidhtD(){
return widhtD;
}
static int getDoorCount(){
return countD;
}
};
class Kambarys{
private:
float heightK;
float widhtK;
float lenghtK;
public:
vector<Langas*> windows;
Durys* door;
Kambarys(float hK, float wK, float lK){
this->heightK=hK;
this->widhtK=wK;
this->lenghtK=lK;
}
Kambarys(const Kambarys &k){
this->door=k.door;
this->windows=k.windows;
heightK=k.heightK;
widhtK=k.widhtK;
lenghtK=k.lenghtK;
}
~Kambarys(){
door=NULL;
for(int i=0; i<windows.size(); i++){
delete windows[i];
}
windows.clear();
delete door;
}
float getHeightK(){
return heightK;
}
float getWidhtK(){
return widhtK;
}
float getLenghtK(){
return lenghtK;
}
void addWindow(Langas* w){
windows.push_back(w);
}
void addDoor(Durys *d){
door=d;
}
};
float countWallPlot(Kambarys* k){
float cWPlot=(2*k->getLenghtK()*k->getHeightK())+(2*k->getWidhtK()*k->getHeightK());
for(int i=0; i<k->windows.size(); i++){
cWPlot-=((k->windows[i]->getHeight()))*(k->windows[i]->getWidht());
}
cWPlot-=((k->door->getHeightD()))*(k->door->getWidhtD());
return cWPlot;
}
float countLenght(Kambarys* k){
float floorL=(k->getLenghtK()*k->getWidhtK()*2);
floorL-=(k->door->getWidhtD());
return floorL;
}
int Langas::countL=0;
int Durys::countD=0;
int main(){
Langas *langas1=new Langas(3.4, 1.2);
Durys *durys=new Durys(3.1, 1.5);
Langas *langas2=new Langas(6.4, 1.5);
Kambarys *k=new Kambarys(30.4, 40.1, 50.1);
Kambarys *k2=k;
k->addWindow(langas1);
k->addWindow(langas2);
k->addDoor(durys);
cout<<countWallPlot(k)<<" "<<countLenght(k)<<endl;
cout<<"Window count "<<Langas::getWindowCount()<<", door count "<<Durys::getDoorCount()<<endl;
k2=k;
delete k;
cout<<countWallPlot(k2)<<" "<<countLenght(k2)<<endl;
cout<<"Window count "<<Langas::getWindowCount()<<", door count "<<Durys::getDoorCount()<<endl;
}
You have to allocate memory for k2 and copy the object, not the pointer.
You have to allocate memory in the copy constructor and copy assignment operator.
door=NULL; before delete door; would skip the delete and cause a memory leak.
windows.clear(); is not necessary in the destructor. Keep your code simple.
EDIT: After you added "Perform the following steps: k2 = * k1; delete k1;" I made k2 an object, not a pointer.
#include <iostream>
#include <vector>
class Langas {
private:
float height;
float width;
static int count;
public:
Langas(float h, float w): height(h), width(w) {
++count;
}
~Langas() { --count; }
float getHeight() const { return height; }
float getWidht() const { return width; }
static int getWindowCount() { return count; }
};
class Durys {
private:
float height;
float width;
static int count;
public:
Durys(float h, float w): height(h), width(w) {
++count;
}
~Durys() { --count; }
float getHeight() const { return height; }
float getWidth() const { return width; }
static int getDoorCount() { return count; }
};
class Kambarys {
private:
float height;
float width;
float length;
public:
std::vector<Langas *> windows;
Durys *door = nullptr;
Kambarys(float hK, float wK, float lK): height(hK), width(wK), length(lK) {}
Kambarys(const Kambarys &k): height(k.height), width(k.width), length(k.length), windows(), door(k.door ? new Durys(k.door->getHeight(), k.door->getWidth()) : nullptr) {
for (const auto window : k.windows) {
windows.emplace_back(new Langas(window->getHeight(), window->getWidht()));
}
}
Kambarys &operator=(const Kambarys &k) {
door = k.door ? new Durys(k.door->getHeight(), k.door->getWidth()) : nullptr;
for (const auto window : k.windows) {
windows.emplace_back(new Langas(window->getHeight(), window->getWidht()));
}
height = k.height;
width = k.width;
length = k.length;
return *this;
}
~Kambarys() {
for (auto window : windows) {
delete window;
}
delete door;
}
float getHeight() const { return height; }
float getWidth() const { return width; }
float getLength() const { return length; }
void addWindow(Langas *w) { windows.emplace_back(w); }
void addDoor(Durys *d) { door = d; }
};
float countWallPlot(const Kambarys &k) {
float cWPlot = 2 * k.getLength() * k.getHeight() + 2 * k.getWidth() * k.getHeight();
for (const auto window : k.windows) {
cWPlot -= window->getHeight() * window->getWidht();
}
cWPlot -= k.door->getHeight() * k.door->getWidth();
return cWPlot;
}
float countLength(const Kambarys &k) {
float floor = k.getLength() * k.getWidth() * 2;
floor -= k.door->getWidth();
return floor;
}
int Langas::count = 0;
int Durys::count = 0;
int main() {
Langas *langas1 = new Langas(3.4, 1.2);
Durys *durys = new Durys(3.1, 1.5);
Langas *langas2 = new Langas(6.4, 1.5);
Kambarys *k = new Kambarys(30.4, 40.1, 50.1);
Kambarys k2(*k);
k->addWindow(langas1);
k->addWindow(langas2);
k->addDoor(durys);
std::cout << countWallPlot(*k) << " " << countLength(*k) << std::endl;
k2 = *k;
std::cout << "Window count " << Langas::getWindowCount() << ", door count " << Durys::getDoorCount() << std::endl;
delete k;
std::cout << countWallPlot(k2) << " " << countLength(k2) << std::endl;
std::cout << "Window count " << Langas::getWindowCount() << ", door count " << Durys::getDoorCount() << std::endl;
}

delete object from vector c++ attempting to reference deleted function

I've been looking around to see if anyone has a similar issue to me and i couldn't find anything. I am prompting the user to type in the name of the planet they wish to delete. My function will locate the position of the planet within the vector objects.
The purpose of this function is to delete a object based on the position i pass through the function parameters.
Planet Class
class Planet {
private:
string name;
double diameter;
double mass;
public:
const double G = 6.67408e-11;
void setName(string n);
bool setDiameter(double d);
bool setMass(double m);
string getName();
double getDiameter();
double getMass();
double CalcSa();
double CalcV();
double CalcDensity();
double CalcG();
string InputS(string x);
double InputD(string x);
Planet();
};
double ReadDouble(double input) {
//Verifys that that user entered in a correct number
while (cin.fail() != 0) {
cerr << "Enter a valid number: ";
cin.clear();
cin.ignore(255, '\n');
cin >> input;
}
return input;
}
string Planet::InputS(string x) {
string user_input;
cout << x;
cin >> user_input;
return user_input;
}
double Planet::InputD(string x) {
double user_input;
cout << x;
cin >> user_input;
user_input = ReadDouble(user_input);
return user_input;
}
Planet::Planet() {
name;
diameter = 0.0;
mass = 0.0;
}
void Planet::setName(string n) {
name = n;
}
bool Planet::setDiameter(double d) {
bool rv = false;
if (d > 0.0) {
rv = true;
diameter = d;
}
return rv;
}
bool Planet::setMass(double m) {
bool rv = false;
if (m > 0.0) {
rv = true;
mass = m;
}
return rv;
}
string Planet::getName() {
return name;
}
double Planet::getMass() {
return mass;
}
double Planet::getDiameter() {
return diameter;
}
double Planet::CalcSa() {
double sa = 4.0 * M_PI * pow((diameter / 2.0), 2.0);
return sa;
}
double Planet::CalcV() {
double v = (4.0 / 3.0) * M_PI * pow((diameter / 2.0), 3.0);
return v;
}
double Planet::CalcDensity() {
double den = mass / CalcV();
return den;
}
double Planet::CalcG() {
double r = diameter / 2.0;
double grav = (G * mass) / (pow(r, 2.0));
return grav;
}
My issue is with this line of code:
l.erase(l.begin() + n);
void DeleteVector(vector<Planet>& l, int n) {
if (int len = l.size() > 0) {
cout << l[n].getName() << " was removed from the list.\n";
l.erase(l.begin() + n);
}
}
i pass in the vector of planets which is a class. and then i pass in the position "n" which i wish to remove from the vector of objects.
I get the following error:
Error C2280 'Planet &Planet::operator =(const Planet &)': attempting to reference a deleted function
Any help or guidance would be appreciated.
The copy assignment operator for your class Planet is implicitly deleted by your compiler, because it has a const member.
See Deleted implicitly-declared copy assignment operator:
A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member of non-class type (or array thereof) that is const;
You probably meant G to be static const:
static const double G;
and then outside of your class:
const double Planet::G = 6.67408e-11;

Using for_each to call a print function from a list of object

I need to use a for_each function to call the print function of each object in the list of objects shapeList. When I put function output as the final parameter of for_each, I get a "cannot determine which instance of overloaded function "output" is intended.
void output(Point* point)
{
point->print();
}
This is my output function for for_each
for_each(shapeList.begin(), shapeList.end(), output);
The for_each statement
I have looked at other solutions that involve using binds and lambdas, but this is a class assignment and I cannot use those methods.
Any help is greatly appreciated.
#include <iostream>
#include <string>
#include <fstream>
#include <list>
#include <algorithm>
#define sz 12
using namespace std;
class Point
{
private:
int x, y;
public:
Point() { }
Point(int a, int b)
:x(a), y(b) { }
// print function is pure virtual and that makes class Point an abstract class
// a pure virtual function can have prototype only without definition
// an abstract class can't be instantiated
// its derived class must override this function in order to be a real class
virtual void print() const = 0;
};
void Point::print() const
{
cout << "\nPoint: ( "
<< x
<< " , "
<< y
<< " )";
}
///////////////////////////////////////////////////////////////////
class Circle : public Point
{
private:
int radius;
public:
Circle() : Point() { }
Circle(int a, int b, int c)
:Point(a, b), radius(c) { }
virtual void print() const;
};
void Circle::print() const
{
cout << "\nCenter of the Circle is at: ";
Point::print();
cout << "\nRadius of the Circle is: "
<< radius;
}
/////////////////////////////////////////////////////////////////////
class Cylinder : public Circle
{
private:
int height;
char color[sz];
public:
Cylinder() { }
Cylinder(int a, int b, int r, int h, char clr[])
: Circle(a, b, r), height(h)
{ strcpy(color, clr); }
virtual void print() const;
};
void Cylinder::print() const
{
Circle::print();
cout << "\nHeight of Cylinder is: "
<< height
<< "\nColor of Cylinder is: "
<< color
<< endl;
}
void load_list(list<Point*>&, char*); //
void output(Point*&);
///////////////////////////////////////////////////////////////
int main()
{
char clr[10];
list<Point*> shapeList;////
load_list(shapeList, clr);
for_each(shapeList.begin(), shapeList.end(), output);
return 0;
}
void load_list(list<Point*>& ptList, char *ch)
{
char type;
int x, y, r, h;
ifstream infile("shapes.txt");
if (!infile)
{
cout << "\nCan not open input file.";
exit(1);
}
infile >> type;
while (infile)
{
if (type == 'c')
{
infile >> x >> y >> r;
ptList.push_back(new Circle(x,y,r));
}
else if (type = 'l')
{
infile >> x >> y >> r >> h >> ch;
ptList.push_back(new Cylinder(x, y, r, h, ch));
}
infile >> type;
}
}
void output(Point* point)
{
point->print();
}
You declare the function to take a pointer by reference(?) And the implementation takes a pointer.

Object Array of Derived Objects

I have an array of objects which all derive from the class BaseStudent.
BaseStudent**studentlist = new BaseStudent*[atoi(listSize.c_str())];
That array is populated with either derived Math, English or History objects. I'm now trying to print out specific data from each object in the array and output it to a file.
for (int j=0; j<atoi(listSize.c_str()); j++){
if(studentlist[j]->getMT() == ENGLISH){
output << studentlist[j]->GetFN()<<" "<<studentlist[j]->GetLN();
output << right << setw(42) << studentlist[j]->GetFinal(); // this is an English public function but I can't call this.
}
}
I need to be able to access the derived classes private member data from the array of objects.
Here's my header. As you can see I have a setter and getter for every protected member data.
#include <iostream>
#include <string>
using namespace std;
#ifndef BASESTUDENT_H
#define BASESTUDENT_H
enum MajorType {ENGLISH, HISTORY, MATH};
// *********************************************************************
// Base class. All other classes (Enlish, History, Math) inherit from
// this class.
// *********************************************************************
class BaseStudent
{
public:
BaseStudent();
BaseStudent(string fn, string ln, string m);
string GetFN(){return firstName;}
string GetLN(){return lastName;}
MajorType getMT(){return course;}
void SetFN(string fn){firstName = fn;}
void SetLN(string ln){lastName = ln;}
void SetMT(string m);
protected:
string firstName;
string lastName;
MajorType course;
}; // End Base class
// *********************************************************************
// Enlish class.
// *********************************************************************
class English: public BaseStudent
{
public:
English(string fn, string ln, string m, double a, double p, double mt, double f);
double FinalAverage();
double GetAttendance(){return attendance;}
double GetProject(){return project;}
double GetMidterm(){return midterm;}
double GetFinal(){return final;}
double GetFinalAverage(){return finalAverage;}
void SetAttendance(double a){attendance = a;}
void SetProject(double p){project = p;}
void SetMidterm(double m){midterm = m;}
void SetFinal(double f){final = f;}
void SetFinalAverage(double fa){finalAverage = fa;}
protected:
double attendance;
double project;
double midterm;
double final;
double finalAverage;
}; // End English class
// *********************************************************************
// History class.
// *********************************************************************
class History: public BaseStudent
{
public:
History(string fn, string ln, string m, double t, double mt, double f);
double FinalAverage();
double GetTermPaper(){return termPaper;}
double GetMidterm(){return midterm;}
double GetFinalExam(){return finalExam;}
double GetFinalAverage(){return finalAverage;}
double FinalExam(){return finalExam;}
void SetTermPaper(double t){termPaper = t;}
void SetMidterm(double m){midterm = m;}
void SetFinalExam(double f){finalExam = f;}
void SetFinalAverage(double fa){finalAverage = fa;}
protected:
double termPaper;
double midterm;
double finalExam;
double finalAverage;
}; // End History class.
// *********************************************************************
// Math class.
// *********************************************************************
class Math: public BaseStudent
{
public:
Math(string fn, string ln, string m, double q1, double q2, double q3,
double q4, double q, double t1, double t2, double f);
double FinalAverage();
double GetQuiz1(){return quiz1;}
double GetQuiz2(){return quiz2;}
double GetQuiz3(){return quiz3;}
double GetQuiz4(){return quiz4;}
double GetQuiz5(){return quiz5;}
double GetFinalExam(){return finalExam;}
double GetTest1(){return test1;}
double GetTest2(){return test2;}
double GetQuizAverage(){return quizAverage;}
double GetFinalAverage(){return finalAverage;}
void SetQuiz1(double q){quiz1 = q;}
void SetQuiz2(double q){quiz2 = q;}
void SetQuiz3(double q){quiz3 = q;}
void SetQuiz4(double q){quiz4 = q;}
void SetQuiz5(double q){quiz5 = q;}
void SetTest1(double q){test1 = q;}
void SetTest2(double q){test2 = q;}
void SetFinalExam(double q){finalExam = q;}
void SetQuizAverage();
void SetFinalAverage(double fa){finalAverage = fa;}
protected:
double quiz1;
double quiz2;
double quiz3;
double quiz4;
double quiz5;
double test1;
double test2;
double finalExam;
double quizAverage;
double finalAverage;
}; // End Math class.
#endif
Do I need some sort of implementation of virtual functions?
Here's my driver so far:
#include<iostream>
#include<fstream>
#include<string>
#include<iomanip>
#include"basestudent.h"
using namespace std;
int main(void)
{
string listSize;
string fileIn = "";
string fileOut = "";
string firstname ="";
string lastname ="";
string major = "";
string eolDummy;
int mQuiz1, mQuiz2, mQuiz3, mQuiz4, mQuiz5, mTest1, mTest2, mFinalExam;
int eAttendance, eProject, eMidterm, eFinalExam;
int hTermPaper, hMidterm, hFinalExam;
ifstream input;
ofstream output;
do{
input.clear();
cout << "Please enter the filename: ";
cin >> fileIn;
cout << "Please enter an output name: ";
cin >> fileOut;
input.open(fileIn);
if (!input)
cout << "Invalid file, please enter again." << endl;
} while(!input);
input >> listSize;
BaseStudent**studentlist = new BaseStudent*[atoi(listSize.c_str())];
int i = 0;
while (!input.eof())
{
getline(input, lastname, ',');
getline(input, firstname, '\n');
input >> major;
if (major == "Math") {
input >>mQuiz1>>mQuiz2>>mQuiz3>>mQuiz4>>mQuiz5>>mTest1>>mTest2
>>mFinalExam>>eolDummy;
// Math Constructor call
// Array += object
studentlist[i] = new Math(firstname,lastname,major,mQuiz1,mQuiz2,mQuiz3,mQuiz4,mQuiz5,
mTest1,mTest2,mFinalExam);
}
else if (major == "History"){
input >>hTermPaper>>hMidterm>>hFinalExam>>eolDummy;
// History Constructor call
// Array += object
studentlist[i] = new History(firstname,lastname,major,hTermPaper,hMidterm,hFinalExam);
}
else if(major == "English"){
input >>eAttendance>>eProject>>eMidterm>>eFinalExam>>eolDummy;
// English Constructor call
// Array += object
studentlist[i] = new English(firstname,lastname,major,eAttendance,eProject,eMidterm,eFinalExam);
}
i++;
}
output.open(fileOut);
output << "Student Grade Summary" << endl;
output << "---------------------" << endl << endl;
output << "ENGLISH CLASS "<<endl<<endl;
output << "Student Final Final Letter"<<endl;
output << "Name Exam Avg Grade"<<endl;
output << "----------------------------------------------------------------"<<endl;
for (int j=0; j<atoi(listSize.c_str()); j++){
if(studentlist[j]->getMT() == ENGLISH){
output << studentlist[j]->GetFN()<<" "<<studentlist[j]->GetLN();
output << right << setw(42) << studentlist[j]->
input.close();
output.close();
return 0;
}
When you take the pointer from your array, you need to cast it using dynamic_cast to the appropriate class
e.g.
BaseStudent *p = somearray[0];
if ( English* pEnglish = dynamic_cast<English*>(p) )
{
// call the methods
cout << p->FinalAverage();
...
}
else if ( History* pHistory = dynamic_cast<History*>(p) )
{
// call the methods
}
else if ( Math* pMath = dynamic_cast<Math*>(p) )
{
// call the methods
}
btw use a vector instead of a raw array, it is more convenient and safer.
std::vector<BaseStudent*> yourvector;

assign member based on string value

I need start off with code because I am not sure what terminology to use. Lets say I have the following code:
class Node
{
public:
void Parse(rapidxml::xml_node<> *node)
{
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
std::stringstream converter;
converter << attr->value();
if( !strcmp(attr->name(), "x") ) converter >> x;
else if( !strcmp(attr->name(),"y") ) converter >> y;
else if( !strcmp(attr->name(), "z") ) converter >> z;
}
}
private:
float x;
float y;
float z;
};
What I can't stand is the repetition of if( !strcmp(attr->name(), "x") ) converter >> x; I feel that this is error prone and monotonous, but I cannot think of another way to map a string value to a member assignment. What are some other approaches one can take to avoid code such as this? The only other possible alternative I could think of was to use a hashmap, but that runs into problems with callbacks
This is the best I could up with but it's not as flexible as I'd like:
class Node
{
Node() : x(0.0f), y(0.0f), z(0.0f)
{
assignmentMap["x"] = &x;
assignmentMap["y"] = &y;
assignmentMap["z"] = &z;
}
public:
void Parse(rapidxml::xml_node<> *node)
{
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
map<std::string, float*>::iterator member = assignmentMap.find(attr->name());
//check for a pre-existing entry
if( member == assignmentMap.end()) continue;
std::stringstream converter;
converter << attr->value();
converter >> *(member->second);
}
}
private:
float x;
float y;
float z;
std::map<std::string, float*> assignmentMap;
};
For the implementation with a map, you could use pointers-to-members. Then you won't need a deep copy of the map (when you copy it, the pointers in the map still point into the original Node), and it will also allow you to make the whole thing static (this map is unnecessary at per-instance basis).
For example:
class Node {
//...
static std::map<std::string, float Node::*> initVarMap();
static float Node::* varFromName(const std::string& name);
};
std::map<std::string, float Node::*> Node::initVarMap()
{
std::map<std::string, float Node::*> varMap;
varMap["x"] = &Node::x;
varMap["y"] = &Node::y;
varMap["z"] = &Node::z;
return varMap;
}
float Node::* Node::varFromName(const std::string& name)
{
static std::map<std::string, float Node::*> varMap = initVarMap();
std::map<std::string, float Node::*>::const_iterator it = varMap.find(name);
return it != varMap.end() ? it->second : NULL;
}
Usage:
float Node::* member(varFromName(s));
if (member)
this->*member = xyz;
This isn't any more flexible, though.
To support different types of members, you might modify the above to use a map of string to "variant of all supported member types".
For example so. The member setter visitor should be reusable, and the only change to the code, to add or change member types, should be done to the typedef.
#include <map>
#include <string>
#include <iostream>
#include <boost/variant.hpp>
template <class Obj, class T>
struct MemberSetter: boost::static_visitor<void>
{
Obj* obj;
const T* value;
public:
MemberSetter(Obj* obj, const T* value): obj(obj), value(value) {}
void operator()(T Obj::*member) const
{
obj->*member = *value;
}
template <class U>
void operator()(U Obj::*) const
{
//type mismatch: handle error (or attempt conversion?)
}
};
class Node
{
public:
Node() : i(0), f(0.0f), d(0.0f)
{
}
template <class T>
void set(const std::string& s, T value)
{
std::map<std::string, MemberTypes>::const_iterator it = varMap.find(s);
if (it != varMap.end()) {
boost::apply_visitor(MemberSetter<Node, T>(this, &value), it->second);
} //else handle error
}
void report() const
{
std::cout << i << ' ' << f << ' ' << d << '\n';
}
private:
int i;
float f;
double d;
typedef boost::variant<int Node::*, float Node::*, double Node::*> MemberTypes;
static std::map<std::string, MemberTypes> initVarMap();
static std::map<std::string, MemberTypes> varMap;
};
int main()
{
Node a;
a.set("i", 3);
a.set("d", 4.5);
a.set("f", 1.5f);
a.report();
}
std::map<std::string, Node::MemberTypes> Node::initVarMap()
{
std::map<std::string, Node::MemberTypes> varMap;
varMap["i"] = &Node::i;
varMap["f"] = &Node::f;
varMap["d"] = &Node::d;
return varMap;
}
std::map<std::string, Node::MemberTypes> Node::varMap = Node::initVarMap();
This is naturally just an example of what you can do. You can write a static_visitor to do what you want. E.g storing a stream and attempting to extract a value of the right type for the given member.
Use an array. An alternative to this union would be to let x, y, and z be references (float&) to array elements 0, 1, 2 — or (my preference) always call them by number not by name.
class Node
{
public:
void Parse(rapidxml::xml_node<> *node)
{
std::stringstream converter;
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
if ( strlen( attr->name() ) != 1
|| *attr->name() < 'x' || *attr->name() > 'z' )
throw rapidxml::parse_error; // or whatever
converter << attr->value() >> ary[ *attr->name() - 'x' ];
}
}
private:
union {
float ary[3]; // this can come in handy elsewhere
struct {
float x;
float y;
float z;
} dim;
};