Storing a pointer to an objects member variable

Started by
11 comments, last by rip-off 12 years, 10 months ago
Hello

I have a problem that has been annoying me for a while & I have yet to come up with a solution. I would like to create a container class that stores objects & sorts them according to ONE of their member variable values. So if I have a class of Students (with the member variable int studentID), I want to store them in my Container class according to their studentID value in ascending order. I have made the container class a templated class so I can use for any class in any of my projects.

My problem with my Container class: I cannot store pointers of objects(for example Student objects). The problem more precisely is that I cannot store a reference(?) to an objects member variable (for example a reference/pointer to a Student's studentID variable).

This problem has been annoying me for ages & any information or advice would be highly appreciated. Is there a way to make my below Container class store pointers to objects member variables? Is there a different way to create a container of objects that can be sorted by their member variables?


#include <iostream>

using namespace std;

template <typename Object, typename dataType>
class Collection
{
public:

Collection( dataType Object::*nMemberVariable )
{
memberVariable = nMemberVariable;
}

bool store( Object* o )
{
// check that o is not a NULL pointer & not already present in maps
if ( o==NULL || instanceVarMap.find(o->*memberVariable) != instanceVarMap.end() )
{
return false;
}

instanceVarMap.insert( o->*memberVariable, o );
return true;
}

private:
dataType Object::* memberVariable;
std::map <dataType, Object*> instanceVarMap;
};


struct FoodItem
{
unsigned int ID;
string name;
double price;
};


int main()
{

// I am attempting to store a pointer to an objects member variable
// this is so I can create a custom container class(like a map or vector) that
// sorts its contents (which are FoodItem objects) according to their member variable values
// so a container could sort all its elements according to a FoodItems ID value or name value

Collection <FoodItem*> foodCol( &FoodItem::name );

string nNames[] = {"a", "b", "c", "d"};
double nPrices[] = {1.1, 2.2, 3.3, 4.4};

for (int i=0; i<4; i++)
{
FoodItem *f = new FoodItem() { i, nNames, nPrices };
foodCol.store( f );
}

// Note storing an ACTUAL object is possible with this class
Collection

system("PAUSE");
return 0;
}


Advertisement
C++ does have member variable pointers, but they're not used that often, and in any case they're not how you'd typically solve a problem of this sort.

I only glanced over the code, but I'll go ahead and mention that the typical solution is to use a sorting function such as std::sort(), and either implement the < operator for the type in question, or implement a custom comparator. (You'll want to use the latter method if the objects are stored by pointer or smart pointer; also, using a custom comparator is arguably more clear, since overloading the < operator for objects that wouldn't normally be 'less-than comparable' can be a little confusing.)
What if I store the member variable reference like this

dataType Object::** memberVariable;


#include <iostream>
#include <map>
#include <string>

using namespace std;

template <typename Object, typename dataType>
class Collection
{
public:

Collection( dataType Object::*nMemberVariable )
{
memberVariable = nMemberVariable;
}

bool store( const Object& o )
{
// check that o is not a NULL pointer & not already present in maps
if ( o==NULL || instanceVarMap.find(o.*memberVariable) != instanceVarMap.end() )
{
return false;
}

instanceVarMap.insert( o.*memberVariable, o );
return true;
}

private:
dataType Object::* memberVariable;
std::map <dataType, Object*> instanceVarMap;
};


struct FoodItem
{
unsigned int ID;
string name;
double price;
};


int main()
{

// I am attempting to store a pointer to an objects member variable
// this is so I can create a custom container class(like a map or vector) that
// sorts its contents (which are FoodItem objects) according to their member variable values
// so a container could sort all its elements according to a FoodItems ID value or name value

Collection <FoodItem*, string> foodCol( &FoodItem::name );

string nNames[] = {"a", "b", "c", "d"};
double nPrices[] = {1.1, 2.2, 3.3, 4.4};

for (int i=0; i<4; i++)
{
FoodItem *f = new FoodItem() { i, nNames, nPrices };
//FoodItem f = {i, nNames, nPrices };
foodCol.store( &f );
}

system("PAUSE");
return 0;
}


Not sure if i understood but you could use inheritance and base all objects on a base class with that member? Like make the container take in a pointer to an object of type ContainsMyWeirdSortingVariable

o3o


Hello

I have a problem that has been annoying me for a while & I have yet to come up with a solution. I would like to create a container class that stores objects & sorts them according to ONE of their member variable values. So if I have a class of Students (with the member variable int studentID), I want to store them in my Container class according to their studentID value in ascending order.

With a little coaxing, std::set will do this:


#include <set>
#include <functional>
#include <string>
#include <iostream>

// This can be reused for any member variable type
template<typename T, typename MemberType>
struct memvar_compare : public std::binary_function<T, T, bool>
{
const MemberType T::*memvar_ptr;
memvar_compare(const MemberType T::* memvar_ptr) : memvar_ptr(memvar_ptr) { }
bool operator() (const T &lhs, const T &rhs) const
{
return (lhs.*memvar_ptr) < (rhs.*memvar_ptr);
}
};

struct person
{
person(const std::string &name, unsigned age) : name(name), age(age) { }

std::string name;
unsigned age;
};

int main()
{
typedef memvar_compare<person, std::string> person_compare;
std::set<person, person_compare> people(&person::name);

//typedef memvar_compare<person, unsigned> person_compare;
//std::set<person, person_compare> people(&person::age);

people.insert(person("Mozart", 35));
people.insert(person("Gandhi", 78));
people.insert(person("Xerces I", 54));
people.insert(person("Me", 29)); // delusions of grandure :)

for (std::set<person, person_compare>::const_iterator it = people.begin(), e = people.end(); it != e; ++it)
std::cout << it->name << ", age " << it->age << '\n';

return 0;
}
Also std::map<key, value> will hold them already sorted by key. You can then transverse it backwards.
[size="2"]I like the Walrus best.
Just to reiterate jyk's advice, as crowbarring the use of a particular container in order to get this behaviour seems a bit daft:

[source lang="c++"]
struct FoodItem
{
FoodItem(unsigned int ID,string name,double price) : ID(ID),name(name),price(price) { }

unsigned int ID;
string name;
double price;
};

bool Less(const FoodItem &a,const FoodItem &b)
{
return a.ID<b.ID;
}

void f()
{
std::vector<FoodItem> items; // or pretty much any other standard container you want

items.push_back(FoodItem(10,"chips",12.3));
items.push_back(FoodItem(5,"bread",14.3));

std::sort(items.begin(),items.end(),Less);
}
[/source]

While a std::map would actually insert the items in sorted order, as owl points out, the additional overhead of storing such small POD types in a map would almost certainly vastly outweigh this benefit.

Also, since when did this work? Have I missed something?

[source lang="c++"]
FoodItem *f = new FoodItem() { i, nNames, nPrices };
[/source]
It still sounds like you want a database. Have you considered such a solution?

What are your requirements if the value of the member changes over time?



Also, since when did this work? Have I missed something?

FoodItem *f = new FoodItem() { i, nNames, nPrices };

[/quote]
Looks like a C++0x initialiser list.

Looks like a C++0x initialiser list.


Oh, okay. That's quite groovy actually.

It still sounds like you want a database. Have you considered such a solution?

What are your requirements if the value of the member changes over time?



Also, since when did this work? Have I missed something?

FoodItem *f = new FoodItem() { i, nNames, nPrices };


Looks like a C++0x initialiser list.
[/quote]

Thanks for everyones replies.

Yes a database would be best but I am using Mosync(Cross Platform Mobile API) & it has limitations. These limitations being, no SQL support (I can write it myself but I dont have time, it does have XML support tho), no STL support (it has its own Set, Queue, Stack, Vector & Maps tho), no threading & doesn't support C++ exceptions.

So to me the best alternative would be a templated container that can be arranged by an object's specific data member (I thought this was a pretty smart way to do this; write it once then use it in many apps). I will look into using the MAUtil::Set class for this tho.

This topic is closed to new replies.

Advertisement