Dynamically changing a property of a class in C++ - c++

Working on a console application and I am trying to figure out a way that allows users to enter an object property along with a value. For example
class Box{
public:
int height;
int width;
int length;
};
int main(){
string memberName,value
cin>>memberName>>value;
}
If a user inputs height, then by using memberName and value how can I change height's value to whatever the user input. I want to make this work so that one can add another class and get the same functionality.
I am considering using maps, but not entirely sure how would I go on doing that.

Here's a complex way to do it, but it does exactly what you were trying to do in the first place (easily apply it to any structure):
#include <iostream>
#include <string>
#include <map>
/////////////////////////////////////////
// HELPERS
template<class T>
class MemberPtrBase
{
public:
virtual std::istream& Read(std::istream& is, T& object) = 0;
virtual ~MemberPtrBase() {}
};
template<class T, class V>
class MemberPtr : public MemberPtrBase<T>
{
V T::*member;
public:
MemberPtr(V T::*ptr)
: member(ptr)
{}
std::istream& Read(std::istream& is, T& object)
{
return is >> (object.*member);
}
};
template<class T>
class MemberMap
{
typedef std::map<std::string, MemberPtrBase<T>*> MapType;
MapType members;
public:
MemberMap() {}
~MemberMap()
{
for (MapType::iterator it = members.begin(); it != members.end(); ++it)
delete it->second;
}
template<class V>
void Register(std::string const& name, V T::*ptr)
{
members[name] = new MemberPtr<T, V>(ptr);
}
std::istream& ReadValue(std::istream& is, T& object)
{
std::string name;
is >> name;
if (members.find(name) == members.end())
{
std::cerr << "Unknown member: " << name << std::endl;
return is;
}
return members[name]->Read(is, object);
}
};
///////////////////////////////////////////
class Box
{
public:
int width;
int height;
int length;
static MemberMap<Box> members;
};
MemberMap<Box> Box::members;
class Circle
{
public:
int x;
int y;
int radius;
static MemberMap<Circle> members;
};
MemberMap<Circle> Circle::members;
int main()
{
Box::members.Register("width", &Box::width);
Box::members.Register("height", &Box::height);
Box::members.Register("length", &Box::length);
Circle::members.Register("x", &Circle::x);
Circle::members.Register("y", &Circle::y);
Circle::members.Register("radius", &Circle::radius);
Box box;
Box::members.ReadValue(std::cin, box);
return 0;
}

using a map is indeed the way to do it. will edit shortly with code example
#include "stdafx.h"
#include <iostream>
#include <map>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
std::map<std::string,std::string> mapData;
std::string name,value;
std::cout << "Enter Name:\n";
std::cin >> name;
std::cout << "\n\nEnter Data\n";
std::cin >> value;
mapData[name] = value;
return 0;
}
or you can have a class wrapping a map if you need to do various other operations on the map or in between (such as validate the input or do other operations with the input before you store it in the map).
// example of class wrapping a map
class MapContainer {
private:
map<string,string> _map;
public:
void addValue(const string& name, const string& value) {
_map[name] = value;
}
string getValue(const string& name) {
return _map[name];
}
}

Simply, you can't do it in a general way; there's no way to access member variables basing on their name from runtime.
This is typically solved by creating an if-else chain (or switch) that allows you to convert data to information:
char type;
float height;
int age;
cin >> type;
switch(type) {
case 'h': cin >> height; break;
case 'a': cin >> age; break;
}
This is inflexible, but so is your class structure, right? If you want to make it dynamic, creating bindings as in #riv's answer makes little sense; any change in class will need proper clause again. It would be better to simply decide that some fields are dynamically stored on map:
class C {
int iAlwaysHaveThat;
map<variant<int,float,string>> dynamicValues;
};
That way you can parse configuration files such as:
Obj [
a = 5,
b = 3.14,
name = "some string value"
]
And then access them by (depending on actual needs) a function returning optional:
optional<variant<...>> getDynamicValue(string const& name) { ... }
Which could also fill in defaults basing on some other object, for example:
{
// ...
auto v = dynamicValues.find(name);
if (v == dynamicValues.end())
return defaultValuesProvider.getDefault(name);
// ...
}
That's a solution that could be actually useful for some uses, however I would probably embed a script language anyway.

Related

How can I static cast the output of the >> operator in c++?

Or perform any function really.
Currently I have some code that looks a bit like this:
int temp_int;
streamer >> temp_int;
finalObj = static_cast<CustomType>(temp_int);
ints can be cast into CustomTypes (obviously) and finalObj is of type CustomType
streamer's >> operator has not been set up to work with CustomType. Changing this is outside my area of control.
Im looking for a way to put the above code in a single line and avoid using a temporary variable.
When putting custom types back into the streamer, I use this:
streamer << static_cast<int>(someObj);
Thats a lot neater. And it would be great to have the instreaming and outstreaming look symmetrical for easier reading.
Thanks
Just implement an operator>> and make sure name lookup finds it.
struct CustomType
{
int m_value;
CustomType(int v = 0)
: m_value(v)
{}
operator int() const
{
return m_value;
}
};
namespace
{
std::istream& operator>>(std::istream& s, CustomType& out)
{
int temp;
s >> temp;
out = temp;
return s;
}
}
int main()
{
CustomType finalObj;
std::cin >> finalObj;
std::cout << finalObj << '\n';
}
Alternatively, if you aren't sure, if operator>> has already been implemented somewhere in an undesired manner, you could create a wrapper type and implement operator>> for it.
namespace
{
class CustomTypeParsingWrapper
{
public:
CustomTypeParsingWrapper(CustomType& target)
: m_target(target)
{
}
friend std::istream& operator>>(std::istream& s, CustomTypeParsingWrapper const& wrapper)
{
int temp;
s >> temp;
wrapper.m_target = temp;
return s;
}
private:
CustomType& m_target;
};
}
int main()
{
CustomType finalObj;
std::cin >> CustomTypeParsingWrapper(finalObj);
std::cout << finalObj << '\n';
}
Note: The use of the anonymous namespace here is only to avoid conflicts with implementations provided to other translation units. If you want to reuse operator>>, you should put it in the same namespace as CustomType to allow Argument Dependent Lookup to find it. A customTypeParsingWrapper intended to be reused could be put in a namespace of your choice...
One way to achieve what you want is to overload not only << and >> operators, but also the cast operator.
One way to do that is to overload the int() operator for CustomType. This way, you can use put your value back into the streamer easily:
class CustomType
{
private:
int _value;
public:
CustomType(const int &value) : _value(value) {}
// ...
operator int() const { return _value; }
};
This way, the compiler will find the appropriate cast from CustomType to int when using the << operator:
streamer << someObj;
will implicitly cast to int.
Similar overloads could be implemented to achieve the results.
Here is a full snipped to test this:
#include <iostream>
class CustomType
{
private:
int _value;
public:
CustomType(const int &value) : _value(value) {}
// ...
operator int() const { return _value; }
};
int main() {
auto someObj = CustomType(10);
std::cout << someObj;
}
This prints 10 to the standard output.

How to make a priority queue use a variable from custom a class (Ascending/descending)

I am having trouble with using the priority_queue in C++, I have a vector of priority queues, the priority queues contains several Person objects. Now I would like for the priority_queue to prioritize the Person objects based on their age. So I have something like this:
class Person
{
public:
string name;
int height;
int age;
};
std::vector<std::priority_queue<Person*>> Persons;
How do I make sure that whenever a person is added to one of the priority queues, that they are prioritized based on their age? And how would I do it in ascending/descending order?
You actually don't need the additional vector which wraps your priority_queue as the priority_queue itself has 2 additional default arguments:
(first one is the type, in your case Person*), second one is the container type and the third one is the compare predicate.
below you can see using a lambda function as a compare predicate for your priority queue.
#include <vector>
#include <string>
#include <queue>
#include <iostream>
using namespace std;
class Person
{
public:
string name;
int height;
int age;
Person(string n, int h, int a): name(n), height(h), age(a) {}
};
ostream& operator<<(ostream &cout, const Person* p) {
return cout << p->name << " height=" << p->height << " age=" << p->age << " ";
}
int main()
{
auto cmp = [](const Person* pl, const Person* pr) {
return (pl->age < pr->age);
};
priority_queue<Person*, vector<Person*>, decltype(cmp)> persons(cmp);
persons.push(new Person("a", 100, 10));
persons.push(new Person("b", 120, 20));
persons.push(new Person("c", 110, 15));
while (!persons.empty()) {
cout << persons.top() << endl;
persons.pop();
}
return 0;
}
You can pass a predicate as third parameter to detect sort order, declare two predicates for your Person*
struct AscendingPersonPredicate
{
bool operator() ( Person* p1, Person* p2) const
{
return p1->age < p2->age;
}
};
struct DescendingPersonPredicate
{
bool operator() ( Person* p1, Person* p2) const
{
return p1->age > p2->age;
}
};
Then declare your vector as:
std::priority_queue<Person*, vector<Person*>, AscendingPersonPredicate> Persons;
or
std::priority_queue<Person*, vector<Person*>, DescendingPersonPredicate> Persons;
std::priority_queue has an interface for what you require. It takes three template parameters:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
The third one is what you need to change to handle the comparison of elements.
// make a functor to do the comparisons
struct comparator
{
bool operator()(Person* lhs, Person* rhs) const {
// sort by age
return lhs->age < rhs->age; // switch the sign for reverse order
}
};
// have a type alias for convenience (typedef is fine too)
using pqueue = std::priority_queue<Person*, std::vector<Person*>, comparator>;
int main()
{
std::vector<pqueue> persons;
}
You can implement like this :
#include <iostream>
#include <queue>
#include <vector>
#include <string>
using namespace std;
class Person
{
public:
string name;
int height;
int age;
};
struct OrderByAge
{
bool operator() (Person const &a, Person const &b) { return a.age > b.age; }
};
int main()
{
vector<priority_queue<Person, std::vector<Person>,OrderByAge> > personPQVec{ 1 };
Person p1{ "nitendra",5,39 };
Person p2{ "bhosle",6,34 };
Person p3{ "nit",4,33 };
personPQVec[0].push(p1);
personPQVec[0].push(p2);
personPQVec[0].push(p3);
while (!personPQVec[0].empty()) {
cout << "Name: " << (personPQVec[0]).top().name << ", age: " << (personPQVec[0]).top().age << endl;
(personPQVec[0]).pop();
}
system("pause");
return 0;
}

Setting a private struct as a return value

For a class assignment, we are required to have a private struct in a class, but we need to have the same struct as a return value (not a pointer to it). Something along these lines:
private:
struct Employee
{
int id;
string name;
};
public:
struct Employee find(int key);
Is this possible using exclusively the STL?
It can be done but does not make great sense because the interface should be public.
For example
#include <iostream>
#include <string>
struct C
{
private:
struct Employee
{
int id;
std::string name;
};
Employee e = { 1, "First" };
public:
Employee find(int key) const
{
return key == e.id ? e : Employee {};
}
};
int main()
{
C c;
auto e = c.find( 1 );
std::cout << e.name << std::endl;
return 0;
}
The program output is
First

Accessing list of fields and types in a class in c++

Hi i am trying to create a simple ORM in c++ for a project. For this example assuming a simple class as
class userProfile: public BaseOrm
{
public:
string username;
string email;
};
Now base orm has a method save() and migrate(). What i want is when a person calls migrate() all the schema , in this case username and email are populated as db tables and on save they persist on database.
What i am having problem with is how do i get what all fields are defined in the class, like in this example username and email and also there types, string in this case. Any help would be appreciated.
I know there is no reflection in c++, so i don't actually care about the variable name but more on the number of variables and there types to map them with DB.
adding reflection to c++ is not insanely difficult but it does require a reasonably good knowledge of template type deduction and some careful planning.
In this working example I have made a start for you. This framework supports writing the members out to a "statement" class (modelling a database prepared statement).
Similar techniques can be used to build out the SQL generation for CRUD.
No doubt there are already libraries that do this for you...
#include <iostream>
#include <iomanip>
#include <string>
#include <tuple>
#include <utility>
using namespace std;
struct statement
{
void setString(int index, const std::string& value)
{
std::cout << "setting index " << index << " to value " << std::quoted(value) << std::endl;
}
};
struct BaseOrm
{
virtual void serialise(statement& stmt) const = 0;
};
template<class Class>
struct class_tag {
using type = Class;
};
template<const char* Name>
struct name_tag {
static constexpr const char* name() { return Name; }
};
namespace detail {
struct reflection_item_concept
{
virtual const std::string& name() const = 0;
virtual std::string to_archive_string(const void* object) const = 0;
virtual void from_archive_string(void* object, const std::string& as) const = 0;
};
template<class T>
std::string to_archive_string_impl(const T& val) {
return std::to_string(val);
}
const std::string& to_archive_string_impl(const std::string& s) {
return s;
}
template<class NameTag, class Class, class Type>
struct reflection_item : reflection_item_concept
{
reflection_item(Type Class::* mfp) : mfp(mfp) {}
static const class_tag<Class> class_info() { return {}; };
static const char* raw_name() { return NameTag::name(); };
// concept implementation
const std::string& name() const override {
static const std::string s = raw_name();
return s;
}
std::string to_archive_string(const void* object) const override
{
auto& val = (*reinterpret_cast<const Class*>(object)).*mfp;
return to_archive_string_impl(val);
}
void from_archive_string(void* item, const std::string& as) const override
{
// similar mechanism here
}
Type Class::* mfp;
};
}
template<class NameTag, class Class, class Type>
constexpr auto reflection_item(NameTag, Type Class::* mp)
{
return detail::reflection_item<NameTag, Class, Type> { mp };
}
struct class_reflection_concept
{
virtual void serialise(const void* object, statement& stmt) const = 0;
};
namespace detail {
template<class ClassTag, class...ReflectionItems>
struct reflection_impl : class_reflection_concept
{
reflection_impl(ReflectionItems...refs)
: _reflectors(std::make_tuple(refs...))
{}
template<std::size_t...Is>
void serialise_impl(std::index_sequence<Is...>, const void* object,
statement& stmt) const
{
using expand = int[];
void(expand{
0,
(stmt.setString(Is + 1, std::get<Is>(_reflectors).to_archive_string(object)),0)...
});
}
void serialise(const void* object, statement& stmt) const override
{
serialise_impl(std::make_index_sequence<sizeof...(ReflectionItems)>(),
object, stmt);
}
std::tuple<ReflectionItems...> _reflectors;
};
}
template<class ClassTag, class...ReflectionItems>
auto& make_reflection(ClassTag tag, ReflectionItems...items)
{
static const detail::reflection_impl<ClassTag, ReflectionItems...> _ { items... };
return _;
}
const char txt_username[] = "username";
const char txt_email[] = "email";
const char txt_x[] = "x";
class userProfile: public BaseOrm
{
public:
string username = "test username";
string email = "noone#nowhere.com";
int x = 10;
// implement serialisation
void serialise(statement& stmt) const override
{
reflection.serialise(this, stmt);
}
static const class_reflection_concept& reflection;
};
const class_reflection_concept& userProfile::reflection =
make_reflection(class_tag<userProfile>(),
reflection_item(name_tag<txt_username>(), &userProfile::username),
reflection_item(name_tag<txt_email>(), &userProfile::email),
reflection_item(name_tag<txt_x>(), &userProfile::x));
int main()
{
userProfile x;
statement stmt;
x.serialise(stmt);
}
expected results:
setting index 1 to value "test username"
setting index 2 to value "noone#nowhere.com"
setting index 3 to value "10"
What I understand is that you want a generic behaviour for classes which have a variable set of fields.
I suggest you to create a "field" interface which will be stored in your base class with a container (for example a map of [fieldName, fieldInterface]). You still have to implement a behaviour for each field's type, but then you can create any class derived from the base class which have a dynamic set of field.
Here is an example :
#include <iostream>
#include <map>
using namespace std;
//the "Field" interface
class IFieldOrm
{
public:
virtual ~IFieldOrm() {}
virtual void save() = 0;
virtual void migrate() = 0;
};
//your base class
class BaseOrm
{
public:
virtual ~BaseOrm();
virtual void save();
virtual void migrate();
protected:
map<string, IFieldOrm*> m_fields; //prefer a smart pointer if you don't want to mess with raw pointer
};
//base class implementation
void BaseOrm::save()
{
for(auto& f : m_fields)
f.second->save();
}
void BaseOrm::migrate()
{
for(auto& f : m_fields)
f.second->migrate();
}
//don't forget to free your "fields" pointers if you have raw pointers
BaseOrm::~BaseOrm()
{
for(auto& f : m_fields)
delete f.second;
}
//then implement your basic types
//(like string, int, ..., whatever type you want to store in your database)
class StringFieldOrm : public IFieldOrm
{
public:
StringFieldOrm(const string& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
string m_value;
};
void StringFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void StringFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff...
}
class IntFieldOrm : public IFieldOrm
{
public:
IntFieldOrm(int& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
int m_value;
};
void IntFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void IntFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff
}
//and finally implement your final class
//note that this object can be "dynamically extended" by inserting new fields,
//you may want to prevent that and I can think of a solution if you want to
class UserProfile: public BaseOrm
{
public:
UserProfile(const string& username, const string& email, int age);
};
UserProfile::UserProfile(const string& username, const string& email, int age)
{
m_fields["username"] = new StringFieldOrm(username);
m_fields["email"] = new StringFieldOrm(email);
m_fields["age"] = new IntFieldOrm(age);
}
int main(int argc, char* argv[])
{
UserProfile user = UserProfile("Batman", "bw#batmail.com", 30);
user.save();
return 0;
}
create a userProfile variable and access them:
userProfile user;
int main(){
std::cout << user.username;
std::cout << user.email ;
}
this is how you would access them, except for different reasons, not printing them to the screen.

C++ is it possible to have a container of specialized templates objects with different types parameters?

I have a templated class Parameter which can (or must) be specialized.
I want to put all my parameters in a container.
How to do this if my parameters are instanciated with different types?
In the class Container, I would like to have a vector<Parameter*> from different types (int, double, ...) or something equivalent which seems to not possible.
If the Parameter class is derived from a base class, then The Container can declare the vect as vector<Base*>. But in this case, we can do nothing specific in Container::foo.
Below is my source example. One of my parameters is a QString which is not compatible with ostream.
Thanks for your comments.
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
/*
class Base {
};
*/
template<typename T> class Parameter /*: public Base */ {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(Parameter *base) {
vect.push_back(base);
}
void foo() {
/* do something with the parameters */
}
private:
vector<Parameter*> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
}
Many thanks to you comments. I will follow your advice and use boost::any.
Here is the updated version :
#include <boost/any.hpp>
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
template<typename T> class Parameter {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(boost::any base) {
vect.push_back(base);
}
void foo() {
cout << "do something with the parameters\n";
for (vector<boost::any>::iterator i = vect.begin(); i != vect.end(); ++i) {
boost::any a = (*i);
if (a.type() == typeid(Parameter<int>*)) {
Parameter<int> *ai = boost::any_cast<Parameter<int> *>(a);
cout << ai->getFoo() << endl;
} else if (a.type() == typeid(Parameter<QString>*)) {
Parameter<QString> *aq = boost::any_cast<Parameter<QString> *>(a);
cout << aq->getFoo() << endl;
} else {
cout << "unknown type:" << a.type().name() << endl;
}
}
}
private:
vector<boost::any> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
container.foo();
}
The correct solution is to write good enough interface for the Base class so that you can do everything you need to do:
class Base {
public:
virtual void *GetVal() const=0;
virtual void SetVal(void *ptr)=0;
virtual std::string Type() const=0;
virtual std::string GetAsString() const=0;
};
While this might not be what you want, it still allows passing values from one parameter to the next. Once you want the actual value, you do need to know the type on compile-time. Switch-case for the type might help with making it runtime.
You could use Boost.Any which can hold any type of data. You would then use boost::any_cast<> to convert the object back to the correct type.
Other than that, you'll have to go for the base class approach, but as you mentioned, it could be hard to then make Container::foo do anything useful.
One way you could solve this problem is to have all your foo functions take a string as a parameter, then each specific implementation of the function would parse that string and convert it to the correct type.
Edit: Boost.Any example:
#include <iostream>
#include <boost/any.hpp>
int main()
{
boost::any param = 89;
// This will fail because `param` is currently holding an int
// not a char
char ch = boost::any_cast<char>(param);
// This works
int i = boost::any_cast<int>(param);
// You can always change the value and type of what
// `param` is holding
param = "example";
}
Every thing inside a container has to be the same type. I have done something similar to your approach where I made a base class that had some useful generic interface and the derived class was templated. The only other way to approach a solution would involve defining a base class function to return a value to indicate the type and then downcasting the base.