assign member based on string value - c++

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

Related

constant member in class

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)))
{}
// ...
};

C++ Inheritance of functions, passing in arguments

Base class : Employee
Derived class : Regular
Employee.cpp
void Employee::setValue(string id, string name, double s, int n)
{
empID = id;
empName = name;
salary = s;
}
Regular.cpp
void Regular::setValue(string id, string name, double s, int n)
{
annualLeave = n;
}
Employee::setValue() only stores the first 3 arguments passed in, but not int n, too.
I'm supposed to inherit that setValue() in Regular::setValue() and then just pass in the arguments, but this time store int n to annualLeave.
How do I do that?
Or, is there a way for me to set int n in the base class for the child class?
You can call the base class's implementation:
void Regular::setValue(string id, string name, double s, int n) {
annualLeave = n;
return Employee::setValue(std::move(id), std::move(name), s);
}
Otherwise, make base class polymorphic:
struct Employee {
void setValue(string id, string name, double s, int n) {
empID = std::move(id);
empName = std::move(name);
salary = s;
setLeave(n);
}
virtual ~Employee() {}
protected:
virtual void setLeave(int) = 0;
string empID;
string empName;
double salary;
};
struct Regular: Employee {
private:
void setLeave(int n) override { annualLeave = n; }
int annualLeave;
};
If necessary to keep a single-signature setValue function, it is possible to do it like that:
-
Includes:
#include <any>
#include <map>
#include <string>
-
Employee.h:
class CEmployee
{
protected:
virtual void setValue(std::map<std::string, std::any> &info);
int m_empID = 0;
std::string m_empName = {'\0'};
int m_salary = 0;
}
Employee.cpp:
void CEmployee::setValue(std::map<std::string, std::any> &info)
{
std::any item;
item = info["empID"];
if (item.has_value())
m_empID = std::any_cast<int>(item); // int
item = info["empName"];
if (item.has_value())
m_empName = std::any_cast<std::string>(item); // std::string
item = info["salary"];
if (item.has_value())
m_salary = std::any_cast<int>(item); // int
}
-
Regular.h:
class CRegular : public CEmployee
{
public:
void setValue(std::map<std::string, std::any> &info) override;
protected:
std::string m_annualLeave = {'\0'};
}
Regular.cpp:
void CRegular::setValue(std::map<std::string, std::any> &info)
{
std::any item;
CEmployee::setValue(info);
item = info["annualLeave"];
if (item.has_value())
m_annualLeave = std::any_cast<std::string>(item); // std::string
}
-
& call it like that:
void MyClass::HardcodedExample()
{
CRegular regular_employee;
std::map<std::string, std::any> info, update;
info["empID"] = { 100 };
info["empName"] = { std::string("Trump") };
info["salary"] = { 1000000 };
info["annualLeave"] = { std::string("29 Jul 2018") };
regular_employee.setValue(info); // Set all info
// Or:
update["annualLeave"] = { std::string("29 Dec 2018") };
regular_employee.setValue(update); // Update just "annualLeave"
// Or:
update["salary"] = { 1200000 };
update["annualLeave"] = { std::string("04 Jul 2018") };
regular_employee.setValue(update); // Update "salary" & "annualLeave"
}
-
Otherwise, setValue with 3 parameters to base-class, & with 4 parameters to the derived-class (that calls to the base-class with the 3 parameters and sets by itself the 4th one) - similar to what #RemyLebeauis offers - is a better solution.
-
& better to use #define / enum keys instead of string-keys (& change the key-type of the map accordingly), but this is a different issue.

C++ enums with attributes like C#

I have the following C# Code which I need to convert into C++ code. I have searched for a bit on how to do C++ Enums with Attributes and can not figure it out.
Basically I need a way to represent the following simplified C# Code in C++ which would do enums with attributes.
C# Code:
public class PSMNameAttribute : Attribute
{
public string PSMName;
public PSMNameAttribute(string _PSMName) { PSMName = _PSMName; }
}
public class PSMNumberAttribute : Attribute
{
public string PSMNumber;
public PSMNumberAttribute(string _PSMNumber) { PSMNumber = _PSMNumber; }
}
public class PSMNumberNameAttribute : Attribute
{
public string PSMNumberName;
public PSMNumberNameAttribute(string _PSMNumberName) { PSMNumberName = _PSMNumberName; }
}
public enum ShippingMethodsTypes
{
[PSMName("ErrorScriptMed")]
[PSMNumber("-5")]
[PSMNumberName("-5 ErrorScriptMed")]
ErrorScriptMed = -5
,
[PSMName("SpecialHandling")]
[PSMNumber("-1")]
[PSMNumberName("-1 SpecialHandling")]
SpecialHandling = -1
,
[PSMName("Error")]
[PSMNumber("0")]
[PSMNumberName("0 Error")]
Error = 0
}
Could this be done like this:
enum Category{
unknown = -1, meat, poultry, seafood, dairy, vegetable,fruit, grain, sweet
};
typedef struct {
float calories; // calories
float carbonhydrates; // grams
float fat; // grams
float cholesterol; // grams
float sodium; // grams
float protein; // grams
Category category ;
}Food;
and if so how would I call the struct values using the enum?
boost::variant and a few visitors should solve it nicely:
#include <boost/variant.hpp>
#include <iostream>
struct get_number : boost::static_visitor<int> {
template<class Method> int operator()(const Method& m) const { return number(m); }
};
struct get_name : boost::static_visitor<std::string> {
template<class Method> const std::string operator()(const Method& m) const { return name(m); }
};
struct ShippingMethodMed {};
static constexpr int number(ShippingMethodMed) { return -5; }
static std::string name(ShippingMethodMed) { return "ErrorScriptMedMed"; }
struct ShippingMethodSpecialHandling { };
static constexpr int number(ShippingMethodSpecialHandling) { return -10; }
static std::string name(ShippingMethodSpecialHandling) { return "SpecialHandling"; }
struct ShippingMethodError {};
static constexpr int number(ShippingMethodError) { return 0; }
static std::string name(ShippingMethodError) { return "Error"; }
using ShippingMethod = boost::variant<ShippingMethodMed, ShippingMethodSpecialHandling, ShippingMethodError>;
int number(ShippingMethod const& sm) {
return boost::apply_visitor(get_number(), sm);
}
std::string name(ShippingMethod const& sm) {
return boost::apply_visitor(get_name(), sm);
}
std::string number_name(ShippingMethod const& sm) {
return std::to_string(number(sm)) + " " + name(sm);
}
int main()
{
ShippingMethod m = ShippingMethodError();
std::cout << number(m) << std::endl;
std::cout << name(m) << std::endl;
std::cout << number_name(m) << std::endl;
m = ShippingMethodSpecialHandling();
std::cout << number(m) << std::endl;
std::cout << name(m) << std::endl;
std::cout << number_name(m) << std::endl;
}
This is not possible with enums alone.
However I think you could solve this by maintaining a string array/vector/map or a combination of those to your enum like:
enum test
{
first = 0,
second, // = 1
// etc...
};
const char* attributes[] =
{
"first attribute",
"second attribute",
};
then you could retrieve this attribute through
const char* firstattribute = attributes[test::first];

Working with a void pointer

Given the following scenario where my data might be of different type based on some condition.
class myClass {
public:
myclass() {
if (condition1) {
bool boolValue = false;
data = boolValue;
} else if (condition2) {
int intValue = 0;
data = intValue;
} else if (condition3) {
unsigned int unsignedIntValue = 0;
data = unsignedIntValue;
} else if (condition4) {
long longValue = 0;
data = longValue;
} else if (condition5) {
double doubleValue = 0.0;
data = doubleValue;
} else if (condition6) {
float floatValue = 0.0;
data = floatValue;
} else if (condition7) {
char *buffer = new char[10];
data = buffer;
}
}
void* getData() const { return data; }
private:
void *data;
}
As it happens the value that my void pointer points to is strictly within each statement. Therefore what is returned with getData() might not be valid. If I do get the data it is simply because the memory location where I point to is not yet written over.
The solution I have come up with is this:
class myClass {
public:
myclass() {
if (condition1) {
boolValue = false;
data = boolValue;
} else if (condition2) {
intValue = 0;
data = intValue;
} else if (condition3) {
unsignedIntValue = 0;
data = unsignedIntValue;
} else if (condition4) {
longValue = 0;
data = longValue;
} else if (condition5) {
doubleValue = 0.0;
data = doubleValue;
} else if (condition6) {
floatValue = 0.0;
data = floatValue;
} else if (condition7) {
buffer = new char[10];
data = buffer;
}
}
void* getData() const { return data; }
private:
void *data;
bool boolValue;
int intValue;
unsigned int unsignedIntValue;
long longValue;
double doubleValue;
float floatValue;
char *buffer;
}
I was thinking there must be a more elegant way to do this. Any suggestions?
You could use a union to save a few bits in memory, and then use pointer casting to get the value from the union:
#include<iostream>
using namespace std;
class myClass {
public:
myClass(char *str){
data.str = str;
}
myClass(double d){
data.d = d;
}
myClass(float f){
data.f = f;
}
void *getData() { return (void*)&data; }
private:
union {
double d;
float f;
char *str;
} data;
};
int main(){
myClass c(2.0);
cout << *(double*)c.getData() << endl;
myClass f(3.0f);
cout << *(float*)f.getData() << endl;
myClass s("test");
cout << *(char**)s.getData() << endl;
system("pause");
}
/* prints
2
3
test
*/
If you don't need to change the type of the data after you create an object, then you could use a template class:
template <typename T>
class myBaseClass {
public:
// Declare common functions here.
T getData()
{ return data; }
protected:
T data;
protected:
// Disallow constructing instances of this class outside the child classes.
myBaseClass(T val) : data(val) { }
};
template <typename T>
class myClass: public myBaseClass<T> {
public:
myClass() : myBaseClass<T>(0) { }
};
You then specialize for char*:
template <>
class myClass<char*>: public myBaseClass<char*> {
public:
myClass() : myBaseClass(new char[10]) { }
};
You then create instances like this:
myClass<int> a;
myClass<float> b;
myClass<char*> c;
// etc.
int i = a.getData();
float f = b.getData();
char* str = c.getData();

C++ Class Inheritance

I have a question about inheritance. From this source:
gSpan.h
struct Edge {
int from;
int to;
int elabel;
unsigned int id;
Edge(): from(0), to(0), elabel(0), id(0) {};
};
class Vertex
{
public:
typedef std::vector<Edge>::iterator edge_iterator;
int label;
std::vector<Edge> edge;
void push (int from, int to, int elabel) //elabel代表edge label
{
edge.resize (edge.size()+1);
edge[edge.size()-1].from = from;
edge[edge.size()-1].to = to;
edge[edge.size()-1].elabel = elabel;
return;
}
};
class Graph: public std::vector<Vertex> {
public:
typedef std::vector<Vertex>::iterator vertex_iterator;
Graph (bool _directed)
{
directed = _directed;
};
bool directed;
Graph(): edge_size_(0), directed(false) {};
};
gSpan.cpp
std::istream &gSpan::read (std::istream &is)
{
Graph g(directed);
while (true) {
g.read (is);
if (g.empty()) break;
TRANS.push_back (g);
}
return is;
}
graph.cpp
std::istream &Graph::read (std::istream &is)
{
std::vector <std::string> result;
char line[1024];
while (true) {
if (result.empty()) {
// do nothing
} else if (result[0] == "t") {
...
} else if (result[0] == "v" && result.size() >= 3) {
unsigned int id = atoi (result[1].c_str());
this->resize (id + 1);
(*this)[id].label = atoi (result[2].c_str());
...
Why we can use (*this)[id].label in graph.cpp? (*this) is a Graph object.... if we want to use (*this)[id].label shouldn't we have to declare std::vector<Vertex>?
class Graph: public std::vector<Vertex>
This means that the Graph class inherited std::vector<Vertex>, so you can do anything with it that you'd be able to do if it were a vector. And so by creating an instance of Graph, you're effectively declaring a new vector.