Original Post
Hi, I've implemented this class and written a unit test for it. The purpose of this class is to keep hold of a count of the number of users of a resource. The wrapper classes that use this core container are responsible for the rules of loading, which are a story for another day. I have a few questions: 1) How can I ensure this class is optimised? I dont want to do any "premature" optimisation, but Ive found in the past that STL has some strange quirks regarding what is faster that what. 2) This is only the second templated storage class I've written, are there any foot-shooting errors in here that I should be aware of? 3) Is my unit test exhaustative? Im aware that ive not tested getAllObjects, as this function is on probation. (read on) 4) can anybody think of a way to have the class delete each VALUE if and only if its a pointer? I want to put that in the deconstructor. Currently, im using a wrapper class for each type of resource that needs this particular system, and letting the wrapper class iterate through the results of getAllObjects(). I would rather not need to do this. 6) Is this "good" code, in terms of presentation and good practice? Is there anything I should be aware of / stop doing? I tried moving the inline functions into a .cpp file but got some annoying errors, Unit test code:
"error C2955: 'UseCountTable' : use of class template requires template argument list" at the top of each function definition. googling the error got lots of results that seem unrelated to what im doing here.
#pragma once
#include <string>
#include <sstream>
#include <map>
#include <vector>
#include <utility>
#include <iostream>
#include "../Logging/Logging.h"
/*
Templated storage of a set of objects along with a modifiable usage count.
*/
template <class VALUE> class UseCountTable
{
public:
UseCountTable()
{
}
~UseCountTable()
{
}
bool addMember(const std::string& name, int use_count, VALUE &object)
{//return false if action cannot be performed
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it == objects.end())
{
objects[name]=std::pair<int,VALUE>(use_count,object);
return true;
}
else
{
return false;
}
}
bool updateMember(const std::string &name, int change)
{//return false if action cannot be performed.
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it != objects.end())
{
it->second.first += change;
return true;
}
else
{
return false;
}
}
bool deleteMember(const std::string& name)
{//return false if action cannot be performed
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it != objects.end())
{
objects.erase(it);
return true;
}
else
{
return false;
}
}
bool exists(const std::string& name)
{//return false if action cannot be performed
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it != objects.end())
{
return true;
}
else
{
return false;
}
}
bool getMember(const std::string& name, int& use_count, VALUE& object)
{//return false if action cannot be performed
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it != objects.end())
{
use_count = it->second.first;
object = it->second.second;
return true;
}
else
{
return false;
}
}
bool getUseCount(const std::string& name, int& use_count)
{//return false if action cannot be performed
std::map<std::string, std::pair<int,VALUE> >::iterator it = objects.find(name);
if(it != objects.end())
{
use_count = it->second.first;
return true;
}
else
{
return false;
}
}
void consoleDump()
{
std::map<std::string, std::pair<int,VALUE> >::iterator
it = objects.begin(),
end=objects.end();
for(it; it != end; ++it)
{
std::cout<<"NAME:" <<it->first <<" USE COUNT:" <<it->second.first<<std::endl;
}
}
void getAllObjects(std::vector<VALUE> &table)
{
std::map<std::string, std::pair<int,VALUE> >::iterator
it = objects.begin(),
end=objects.end();
for(it; it != end; ++it)
{
table.push_back(it->second);
}
}
private:
std::map<std::string, std::pair<int,VALUE> > objects;
};
void UseCountTable_Test()
{
struct TestObject{std::string name; std::string data; int integer;};
UseCountTable<TestObject> t;
// create 3 objects
TestObject ob1,ob2,ob3;
ob1.name="Object 1"; ob1.data="DATA!!!!!!"; ob1.integer=1;
ob2.name="Object 2"; ob2.data="DATA@@@@@@"; ob2.integer=2;
ob3.name="Object 3"; ob3.data="DATA££££££"; ob3.integer=3;
// insert them with use count of 1
t.addMember("Object 1",1,ob1);
t.addMember("Object 2",1,ob2);
t.addMember("Object 3",1,ob3);
t.consoleDump();
// update object n with n more users
t.updateMember("Object 1",1);
t.updateMember("Object 2",2);
t.updateMember("Object 3",3);
t.consoleDump();
// update objects with -1 users
t.updateMember("Object 1",-1);
t.updateMember("Object 2",-1);
t.updateMember("Object 3",-1);
t.consoleDump();
// get object that should not be there, expect false
TestObject ttest; int itest;
std::cout<<(int) t.getMember("NOT THERE",itest,ttest);
// get object that is there, expect true
std::cout<<(int) t.getMember("Object 1",itest,ttest);
// update object that should not be there, expect false
std::cout<<(int) t.updateMember("hahaha",1);
// update object that is there, exect true
std::cout<<(int) t.updateMember("Object 1",1);
// delete object not there, expect false
std::cout<<(int) t.deleteMember("not there");
// delete object that is there, expect true
std::cout<<(int) t.deleteMember("Object 1");
// delete same object again, expect false
std::cout<<(int) t.deleteMember("Object 1");
// get usecount of object that is there, expect true
std::cout<<(int) t.getMember("Object 2",itest,ttest);
// try to get that object, expect false
std::cout<<(int) t.getMember("Object 1",itest,ttest);
}