Let's say my class has an integer data member:
class MyClass {
public:
MyClass();
private:
int my_int;
}
Now upon construction, I want to receive a data type, a name and a value, all codified as strings:
public MyClass(string type, string name, string value) { ... }
And based on type and name, assign that value to my data member on construction. Something similar to:
public MyClass(string type, string name, string value)
{
this->parse(type, name, value);
}
private parse(string type_, string name_, string value_)
{
if (type_=="int")
{
int* int_ = getDataMember(name_);
(*int_)=atoi(value_);
}
else ....
}
So now when i declared MyClass c("int","my_int","5"), c would be initialized with its my_int data member set to the value of 5.
The problem is I have no idea how to implement getDataMember(string name). But it would obviously return a pointer to its data member of the name "name".
While this isn't how C++ is supposed to be used, it isn't entirely impossible.
#include <string>
#include <map>
class MyClass {
public:
MyClass(const std::string& type, const std::string& name, const std::string& value);
private:
void parse(const std::string& type_, const std::string& name_, const std::string& value_);
template <typename T>
T* getDataMember(const std::string& name);
int my_int;
double my_double;
char my_char;
std::map<std::string, char*> members; //Map of strings to member memory locations
};
MyClass::MyClass(const std::string& type, const std::string& name, const std::string& value)
{
//List of members will have to be hardcoded
members["my_int"] = reinterpret_cast<char*>(&my_int);
members["my_double"] = reinterpret_cast<char*>(&my_double);
members["my_char"] = reinterpret_cast<char*>(&my_char);
this->parse(type, name, value);
}
template <typename T>
T* MyClass::getDataMember(const std::string& name) {
return reinterpret_cast<T*>(members[name]); //Will need to handle invalid names
}
void MyClass::parse(const std::string& type_, const std::string& name_, const std::string& value_)
{
if (type_=="int")
{
int* int_ = getDataMember<int>(name_);
(*int_)=atoi(value_.c_str());
}
}
int main(void) {
MyClass c("int","my_int","5");
return 0;
}
The idea is to keep a map to reference strings to member addresses. Accessing the map with the string will return the address of the member corresponding to that string. However, this map will have to be hardcoded when new members are introduced to the class. Also, the getDataMember function will have to handle cases where invalid names are passed into the class.
Inefficiencies
string comparisons are fundamentally slow. The comparisons occur
when you are inserting members into the map, when you are going
through your parse function trying to identify the type, and when
you searching through the map for the correct key.
Possible ways to get rid of hardcoding
Suppose there is a way to fill the map without hardcoding. That would involve knowing what data members are present in the class. Since there does not seem to be such a functionality in C++, we would have to parse the file, or at least the part of the file that contains the class. However, we need this to be done before the file is compiled. This leaves us with 2 options:
Do it at compile-time, which involves using preprocessor directives. Unfortunately, I have no idea of any way to utilize preprocessor directives to do so.
Program an external executable that parses files in your project/workspace to identify the data members of the class and proceed to append the filling of the member map in the class's constructor(s). i.e. Automate the map filling process through an external executable. We can then ensure that this external executable is always run whenever the project is build through pre-build events.
Conclusion
I think it's best to either find another approach to the problem you are trying to solve, or use a whole other language because it seems C++ was not built for this.
Factoring in what your real problem is
I believe the solution you are looking for is called deserialization.
class MyClass {
public:
void Deserialize(XMLParser xml);
private:
int my_int;
double my_double;
char my_char;
};
void MyClass::Deserialize(XMLParser xml) {
while(xml.ReadLine() != "MEMBERS"); //Advances the XML parser to the line that specifies the members
//Proceed to deserialize the members in order
//This requires the file to list the members in the correct order
my_int = atoi(xml.ReadLine().c_str());
my_double = atof(xml.ReadLine().c_str());
my_char = atoi(xml.ReadLine().c_str());
}
Related
Okay so I'm a bit of a noob at C++ and in my second assignment I am required to make classes with public and private arguments etc, etc. Basically the mutator functions won't work because apparently they're not of type const?
This is the header file with the class:
class Customer {
private:
string PhoneNumber_;
string Name_;
string Address_;
public:
string get_PhoneNumber() const {return PhoneNumber_;} // Accessor
const void set_PhoneNumber(unsigned x) {PhoneNumber_ = x;} // Mutator
string get_Name() const {return Name_;}
const void set_Name(unsigned x) {Name_ = x;}
string get_Address() const {return Address_;}
const void set_Address(unsigned x) {Address_ = x;}
};
// declare the CreateCustomer function prototype with default values
Customer* CreateCustomer(const string& id = BLANK, const string& name = BLANK, const string& address = BLANK);
Customer* CreateCustomer(const string& id, const string& name, const string& address) {
Customer* temp = new Customer();
temp->get_PhoneNumber() = id; // Due to the Accessors and Mutators PhoneNumber, Name and Address are now functions
temp->get_Name() = name;
temp->get_Address() = address;
return temp;
}
And this is the error I get in the main.cpp file:
cout << "\n\nDear ";
cout << Charge[0].Holder.set_Name() << " (" << Charge[0].Holder.set_PhoneNumber() << ")"; // DisplayCustomer(customer) ;
cout << ",\n" << Charge[0].Holder.set_Address() << "\n\n"
Basically, the exact error message is:
Member function 'set_Name' not viable: 'this' argument has type 'const
Customer', but function is not type const
It happens with set_PhoneNumber and set_Address as well. Any help would be greatly appreciated! Thanks!
UPDATE: I got it working. Thanks everyone for helping me out!
If you want to set a value, use the set method. get Methods are only to obtain variables, not to set the inner variables of a class (If they are defined the way you did).
The correct usage is:
Customer* CreateCustomer(const string& id, const string& name, const string& address) {
Customer* temp = new Customer();
temp->set_PhoneNumber( id );
temp->set_Name( name );
temp->set_Address( address );
return temp;
}
Also, you have to alter the interface of your methods:
class Customer {
private:
string PhoneNumber_;
string Name_;
string Address_;
public:
string get_PhoneNumber() const {return PhoneNumber_;} // Accessor
void set_PhoneNumber(const string& x) {PhoneNumber_ = x;} // Mutator
string get_Name() const {return Name_;}
void set_Name(const string& x) {Name_ = x;}
string get_Address() const {return Address_;}
void set_Address(const string& x) {Address_ = x;}
};
Since you want to set strings and not numbers.
Using const string& as function arguments is better than string to not copy the string when passing it as an argument. Since it is a const reference, you don't have to fear the function could manipulate the input.
Ehm. I think you should use get and set in reverse way...
In CreateCustomer you should use set functions and when print Customer to stream - you should use get functions.
And set functions should receives string, not unsigned.
And so, it will be better to use constructor, instead of set functions and then will be only get functions.
You should use std:: in the class declaration. See Why is “using namespace std;” considered bad practice? on the question why.
Your set_ methods take unsigned arguments. You cannot assign an unsigned to a string like PhoneNumber_ = x;. The arguments need to be strings.
You'd need to change your members like
std::string get_PhoneNumber() const { return PhoneNumber_; } // Accessor
const void set_PhoneNumber(std::string const & x) { PhoneNumber_ = x; } // Mutator
When you write temp->get_PhoneNumber() = id; your intention is clearly to set the value for PhoneNumber_, so why do you use the get_ method? Just use the appropriate set_ method and write temp->set_PhoneNumber(id);.
Generally avoid pointers in C++. If you're really in need of a pointer use a smart pointer like std::unique_ptr or std::shared_ptr (if and only if you are required to use a plain pointer: use one).
A 'blank' default value for a std::string is an empty string like
std::string const & id = std::string{} Appears clearer to me.
To create an object of type Customer with blank/empty member strings you do not need to do more than Customer customer_object; since there is an implicitly declared default constructor which uses the std::string default constructor which results in an empty strign anyway.
Usually a constructor is used to create an object depending on some arguments values.
You could easily write one that takes all required values and can be used as a default constructo anyway by adding something along the lines of
Customer(const std::string& id = std::string{},
const std::string& name = std::string{},
const std::string& address = std::string{})
: PhoneNumber_(id), Name_(name), Address_(address)
{ }
to your class. See another C++ Class Initialization List example.
See another C++ Class Initialization List example.
For the sake of encapsulation you usually want to avoid using 'direct' getters and setters revealing your data structure.
You've declared PhoneNumber_, Name_ and Address_ as string.
But in the setter methods, you are passing unsigned (int)
Also, you have reversed the usage of getters and setters!
Also, the return types of setters can be just void and not const void.
I want to define structs to hold various application parameters:
struct Params
{
String fooName;
int barCount;
bool widgetFlags;
// ... many more
};
but I want to be able to enumerate, get and set these fields by name, eg so that I can expose them to automation APIs and for ease in serialisation:
Params p;
cout << p.getField("barCount");
p.setField("fooName", "Roger");
for (String fieldname : p.getFieldNames()) {
cout << fieldname << "=" << p.getField(fieldName);
}
Is there a good way of defining a binding from a string label to a get/set function? Along the lines of this (very much pseudocode):
Params() {
addBinding("barCount", setter(&Params::barCount), getter(&Params::barCount));
...
I know that other options are to auto-generate the struct from an external metadata file, and another is to store the struct as a table of (key,value) pairs, but I would rather keep the data in a struct.
I do have a Variant type which all fields are convertible to.
C++ doesn't have reflection so this isn't something you can do cleanly. Also, by referring to members as strings, you have to try to side-step the strongly typed nature of the language. Using a serialization library like Boost Serializer or Google Protobuf might be more useful.
That said, if we allow some horribleness, one could do something with an XMacro. (Disclaimer: I wouldn't recommend actually doing this). First you put all the information you need into a macro
#define FIELD_PARAMS \
FIELD_INFO(std::string, Name, "Name") \
FIELD_INFO(int, Count, "Count")
Or alternatively into a header file
<defs.h>
FIELD_INFO(std::string, Name, "Name") \
FIELD_INFO(int, Count, "Count")
Then you'll define FIELD_INFO inside your class to either mean the member declaration, or adding them to a map
struct Params{
Params() {
#define FIELD_INFO(TYPE,NAME,STRNAME) names_to_members.insert(std::make_pair(STRNAME,&NAME));
FIELD_PARAMS
#undef FIELD_INFO
}
template <typename T>
T& get(std::string field){
return *(T*)names_to_members[field];
}
std::map<std::string, void*> names_to_members;
#define FIELD_INFO(TYPE,NAME,STRNAME) TYPE NAME;
FIELD_PARAMS
#undef FIELD_INFO
};
And then you could use it like this
int main (int argc, char** argv){
Params myParams;
myParams.get<std::string>("Name") = "Mike";
myParams.get<int>("Count") = 38;
std::cout << myParams.get<std::string>("Name"); // or myParams.Name
std::cout << std::endl;
std::cout << myParams.get<int>("Count"); // or myParams.Count
return 0;
}
Unfortunately you still need to tell the compiler what the type is. If you have a good variant class and libraries that play well with it, you may be able to get around this.
I'm using a slightly different storage for this: here. The tags I use are ints for some reason, but you could use std::string keys just as well.
There is no really good way (with "good" being a very subjective aspect anyway), because whatever technique you choose is not part of the C++ language itself, but if your goal is serialisation, have a look at Boost Serialization.
I've managed to come up with something that satisfies my particular need. Ari's answer was closest in terms of mapping strings to references to member variables, though it relied on casting from void*. I've got something that's a bit more type-safe:
There's an interface for an individual PropertyAccessor that has a templated class derived from it which binds to a reference to a specific member variable and converts to and from the Variant representation:
class IPropertyAccessor
{
public:
virtual ~IPropertyAccessor() {}
virtual Variant getValueAsVariant() const =0;
virtual void setValueAsVariant(const Variant& variant) =0;
};
typedef std::shared_ptr<IPropertyAccessor> IPropertyAccessorPtr;
template <class T>
class PropertyAccessor : public IPropertyAccessor
{
public:
PropertyAccessor(T& valueRef_) : valueRef(valueRef_) {}
virtual Variant getValueAsVariant() const {return VariantConverter<T>().toVariant(valueRef); }
virtual void setValueAsVariant(const Variant& variant) {return VariantConverter<T>().toValue(variant); }
T& valueRef;
};
// Helper class to create a propertyaccessor templated on a type
template <class T>
static IPropertyAccessorPtr createAccessor(T& valueRef_)
{
return std::make_shared<PropertyAccessor<T>>(valueRef_);
}
The class exposing a collection can now define an ID -> PropertyAccessor and bind its values by reference:
#define REGISTER_PROPERTY(field) accessorMap.insert(AccessorMap::value_type(#field, createAccessor(field)))
class TestPropertyCollection
{
public:
typedef std::map<PropertyID, IPropertyAccessorPtr> AccessorMap;
TestPropertyCollection()
{
REGISTER_PROPERTY(stringField1);
// expands to
// accessorMap.insert(AccessorMap::value_type("stringField", createAccessor(stringField)));
REGISTER_PROPERTY(stringField2);
REGISTER_PROPERTY(intField1);
}
bool getPropertyVariant(const PropertyID& propertyID, Variant& retVal)
{
auto it = accessorMap.find(propertyID);
if (it != accessorMap.end()) {
auto& accessor = it->second;
retVal = accessor->getValueAsVariant();
return true;
}
return false;
}
String stringField1;
String stringField2;
int intField1;
AccessorMap accessorMap
};
Suppose you have a namespace
approvedParams {
std::string s1 = "my_string_input_1";
std::string s2 = "my_string_input_2";
}
Outside the scope of approvedParams there exists a function myfun(std::string parm1)
Is it possible to constrain myfun signature to only accept fields of type std::string from the approvedParams namespace?
That is:
myfun("my_string_input_1") will not compile.
myfun(approvedParams::s1) will compile.
I was considering an implementation with enum. However I ultimately want use approvedParams::s1 and s2 when I am parsing key-value configuration files. enum must be of an integral type. I am not interested in adding another unnecessary layer with map<int,std::string> to connect the enum integers with std::string.
The type of s1 and s2 do not carry information about the namespace they were declared in.
You could rather easily wrap a custom type though.
namespace approvedParams
{
struct keytype { std::string val; };
keytype s1 = { "my_string_input_1" };
keytype s2 = { "my_string_input_2" };
}
void approvedParams( approvedParams::keytype );
Create an enum of approved parameter values
enum approvedParams {
val1 = 0,
val2, ...
};
And just create an array with the string using these indexes
std::string[] approvedParamValues = { "my_string_input_1", ... };
Here's my take at it, reusing #DrewDormann's idea of a wrapper type but in a way that actually enforces its usage.
The idea is basically a variant of the named constructor idiom, but only with static variables instead of fully-fledged factory functions (does this pattern have a name?).
// Declaration in .h
class ApprovedParam
{
public:
static const ApprovedParam foo;
static const ApprovedParam bar;
const std::string& value() const { return m_value; }
private:
std::string m_value;
ApprovedParam(const char* value) : m_value(value) {}
ApprovedParam(std::string&& value) : m_value(std::move(value)) {}
ApprovedParam(const std::string& value) : m_value(value) {}
// IMPORTANT: the user must not be able to default construct an ApprovedParam
ApprovedParam() = delete;
};
// Definitions in .cpp
const ApprovedParam ApprovedParam::foo = "foo";
const ApprovedParam ApprovedParam::bar = "bar";
Now the user can copy/move/assign ApprovedParam objects but he has only a limited set of (immutable) instances to choose from and he will not be able to create completely new ones.
In C++, I have an class which is ordered by its name which is a std::string. I wish to only have one per each unique name in either a std::map or std::set.
I could use a std::set since the operator< will order my instances by their name, however, I need to lookup an instance by its name. Using a map where the key is the name is straight forward, however, I could also use a set and construct a dummy instance of my class with the name I wish to lookup to locate in the set the actual instance of the class for the given name.
I imagine I should just go with the map to make the code straight forward, but wonder if there might be a way to go with the set since the key is effectively part of my object anyway and thus avoid some redundancy.
Is there a way to use the set and be able to locate objects by their key in a clean way or should I just use a map and be done with it?
Here is the class to be inserted (in draft form) and in each directory there is either a set or map of Node(s) keyed off the Node's name:
class Node {
public:
Node(Directory &parent, const std::string &name)
: _name(name),
_parent(&parent),
_isRoot(false) {
if (name.empty()) {
throw InvalidNodeNameError(name);
}
}
protected:
// This is only used for the root directory:
Node()
: _name(""),
_parent(0),
_isRoot(true) {
}
Node(const std::string &name)
: _name(name),
_parent(0),
isRoot(false) {
}
public:
virtual ~Node() {
if (parent()) {
parent()->remove(*this);
}
}
bool operator<(const Node &rhs) const {
return _name < rhs._name;
}
Directory *parent() const {
return _parent;
}
void setParent(Directory *parent) {
_parent = parent;
}
const std::string &name() const {
return _name;
}
bool isRoot() const {
return _isRoot;
}
std::string pathname() const {
std::ostringstream path;
if (parent()) {
path << parent()->pathname() << '/';
} else {
path << '/';
}
path << name();
return path.str();
}
private:
// Not defined:
Node(const Node &rhs);
Node &operator=(const Node &rhs);
private:
std::string _name;
Directory *_parent;
const bool _isRoot;
};
Actually, you can just use map<std::string&, Node>, at the cost of one extra pointer, but I think you probably knew that, and it requires some mucking about to get what you want.
I've always thought it was a real pain that std::set didn't come with an explicit KeyExtractor template parameter, particularly since every implementation I've seen uses one of those under the hood in order to not duplicate code between (multi)maps and (multi)sets. Here's a quick and dirty hack, not close to complete, which exposes some of the mechanics of the GNU standard C++ library in order to create a "keyed_set" container:
// Deriving from the tree is probably not a good idea, but it was easy.
template<typename Key, typename Val, typename Extract,
typename Compare = std::less<Key>, typename Alloc = std::allocator<Val>>
class keyed_set : public std::_Rb_tree<Key, Val, Extract, Compare, Alloc> {
using Base = std::_Rb_tree<Key, Val, Extract, Compare, Alloc>;
public:
template<typename ...Args>
auto insert(Args... args)
->decltype(Base()._M_insert_unique(std::declval<Args>()...)) {
return this->_M_insert_unique(args...);
}
typename Base::iterator insert(typename Base::const_iterator i,
const Val& val) {
return this->_M_insert_unique_(i, val);
}
Val& operator[](const Key& key) {
auto i = this->lower_bound(key);
if (i == this->end() || this->key_comp()(key, Extract()(*i))) {
i = this->_M_insert_unique_(i, Val(key));
}
return *i;
}
};
To make this work, you need to provide a Key Extractor, like this:
template<class T>
struct KeyExtractor;
template<>
struct KeyExtractor<Node> {
const std::string& operator()(const Node& n) { return n.name(); }
};
To get my version of operator[] to work, you need the value type to have a constructor which takes its key type as an argument.
I left out lots of stuff (erase, for example); but it was good enough to do a simple test.
It would probably have been better to default the key type from the return type of the KeyExtractor, but that would have involved putting the template arguments in a different order, and I already wasted too much time not noticing that _M_insert_unique and _M_insert_unique_ are spelled differently (presumably to avoid template instantiation problems.)
Here's the example I used to check to make sure that works; MyKeyedClass has a name, with a vector of strings, and a double associated with each one. (There's no sublime purpose.)
int main(void) {
keyed_set<std::string, MyKeyedClass, KeyExtractor<MyKeyedClass>> repo;
for (std::string name, val; std::cin >> name >> val; ) {
try {
size_t end;
double d = std::stod(val, &end);
if (end != val.size())
throw std::invalid_argument("trailing letters");
repo[name].increment(d);
} catch (std::invalid_argument(e)) {
repo[name].push(val);
} catch (std::out_of_range(e)) {
std::cerr << "You managed to type an out of range double" << std::endl;
}
}
std::for_each(repo.begin(), repo.end(),
[](MyKeyedClass& c){ std::cout << c << std::endl; });
return 0;
}
I think that because Node requires a reference to Directory during construction, making a dummy node to search your set by name will make the Node class more cluttered.
To use set you'd probably need to make a static Directory somewhere, and use that as a dummy reference in a new dummy constructor Node(const std::string&). If you don't declare that explicit you can use a string directly in your call to set::find.
You could instead convert the class to use pointers... But that would change its internal semantics: Directory& is always valid, whereas Directory* doesn't have to be. Ask yourself whether you want to make the semantics less clear to the reader simply because of your preference for the set container.
So my opinion is pretty clear in this case... You have a choice: Either use map and keep your class clean, or use set and write a bit of supporting junk code that has no use for anything else. =)
I implement such classes through a proxy for key, for example in case of std::string I have a class called lightweight_string, that implement operator < and internally it point to an std::string then I use map and have both simplicity of using map and performance of not having 2 version of key.
For your very case check if your compiler is old enough to still implement std::string with COW (copy on write) strategy. This changed in C++11, but old compiler versions still are COWs... This has the advantage that it will cost you almost nothing to have map with string as key and as part of value. But be aware this will change in future (or already changed)...
I'm looking at some code at the moment which has been ported and is failing to compile. The code has been written in a rather 'C' like way and is passing function pointers in order to set particular mutators on an object. The object being populated is declared as follows:
class Person
{
std::string n_;
int a_;
public:
void name( const std::string& n ) { n_ = n; }
std::string name() const { return n_; }
void age( const int& a ) { a_ = a; }
int age() const { return a_; }
};
Fairly standard stuff. Then we have some interesting functions which I've trimmed for brevity:
typedef void (Person::FnSetStr)(const std::string& s);
typedef void (Person::FnSetInt)(const int& i);
void setMem( const std::string& label, Person* person, FnSetStr fn)
{
// Do some stuff to identify a std::string within a message from the label.
// assume that 'val_s' contains the string value of the tag denoted by
// the label.
(person->*fn)(val_s);
}
void setMem( const std::string& label, Person* person, FnSetInt fn)
{
// Do some stuff to identify an int within a message from the label.
// assume that 'val_i' contains the int value of the tag denoted by the
// label.
(person->*fn)(val_i);
}
And then this gets called as follows:
Person* person = new Person;
setMem("Name", person, Person::name ); // (1)
setMem("Age", person, Person::age ); // (2)
The idea seems to be to pass a label, an object and the address of an appropriate mutator. The type of the 3rd parameter is being used to get the compiler to select which overload to call and the specific overload then gets a suitable variable ready and calls the function passing it as a parameter to set the value on the object.
This workled on an old Solaris compiler. However, when it compiles on GCC, I get failures at points (1) and (2):
error: no matching function for call to
'setMem( const std::string& label, Person* person, <unknown type> )'
It looks like the new compiler treats, say, Person::age as a type rather than a pointer to a function and cannot resolve the overload. I am looking at changing the code to use a function object rather than straight pointers to functions.
I wanted to know whether there's a way that the calling code can stay like this (i.e. without an explicitly stating the type that the function takes) bearing in mind that I can't change the Person class and would ideally like to keep changes to a minimum.
First change the declaration:
typedef void (Person::*FnSetStr)(const std::string& s);
typedef void (Person::*FnSetInt)(const int& i);
Then change the call:
setMem("Name", person, &Person::name ); // (1)
setMem("Age", person, &Person::age ); // (2)
Builds clean at warning level 4 in VS 2010.