Create a lookup table for array of classes in C++/Arduino - c++

I have multiple types of classes.
Each type has an array and an index in the array.
If an outside function knows only a string ID of a class and wants to use it's public function,
it must search for that particular class by ID in it's array.
This is really inefficient.
All my classes created at runtime and a function which creates it, puts it into an array.
I want to create a lookup table of some sort for this when the classes are created, so any outside
function if wants to use one class it does not have to for loop on the class's array and check each ID if it matches but to be able to reach the class by some struct or array.
Here how it is done now:
#define MAX_ONE_TYPES 20
int createdOneTypesCounter = 0;
// Create one type of classes in one for loop and put it into an array.
// We must keep track of the index because a class can be created later
// at runtime so we must keep increasing the index. I don't check for max index now...
// oneTypes is a JSON OBJECT
for (JsonPair oneTypeRef: oneTypes) {
const char* oneTypeID = oneTypeRef.key().c_str();
JsonObject oneTypeOptions = oneTypes[oneTypeID];
oneTypeClasses[createdOneTypesCounter ] = new oneTypeClass(oneTypeOptions);
createdOneTypesCounter++;
}
class oneTypeClass{
private:
// using an external ram for this kinda stuffs.
const size_t IDS_AND_NAMES_SIZE = 500;
const char * id = (char *) ps_malloc (IDS_AND_NAMES_SIZE * sizeof (char));
public:
thermHandler(JsonObject options){
// She got an ID on creation.
id = strdup(options["id"]);
}
void setModes(boolean mode){
// set some mode...
}
boolean isMyID(const char* packetID){
if( strcmp(id, packetID) == 0 ){return true;}
return false;
}
};
oneTypeClass* oneTypeClasses[MAX_ONE_TYPES] EXT_RAM_ATTR;
// Here comes an outside function. Wants to set a boolean in a class with specific ID.
static const inline void setOneTypeMode(JsonObject packet){
for(int i = 0; i < MAX_ONE_TYPES; i++){
if(oneTypeClasses[i] != NULL && oneTypeClasses[i]->isMyID(packet["id"])){
oneTypeClasses[i]->setModes(packet["mode"]);
break;
}
}
}
And here is my problem. I must search for a class by ID every time some outside function wants to do something with one of the classes.
I don't know how would i do it.
In JS i would create an object for a lookup table and every time a class is created i would put it's ID for the key and it's index to the value like this:
var oneTypeClass_Lookup{
"CLASS ID STRING" : "CLASS INDEX IN ARRAY"
};
//And a function would do it like this:
static const inline void setOneTypeMode(JsonObject packet){
int myClassIndex = oneTypeClass_Lookup[ packet["id"] ];
oneTypeClasses[myClassIndex]->setModes(packet["mode"]);
}
I'm doing this for "mass operation":
static const inline int getOneTypeClassIndex(const char* packetID){
for(int i = 0; i < MAX_THERMS; i++){
if(oneTypeClasses[i] != NULL && oneTypeClasses[i]->isMyID(packetID)){
return i;
}
}
return -1;
}
static const inline void setThing(int newThing, const char* packetID){
int index = getOneTypeClassIndex(packetID);
if( index > -1 ){
oneTypeClasses[index]->setNewThing(newThing);
}
}
static const inline void setThing_Two(int newThing, const char* packetID){
int index = getOneTypeClassIndex(packetID);
if( index > -1 ){
oneTypeClasses[index]->setNewThing(newThing);
}
}
But i can't do this in C or Arduino C++. I hope i was clear.
UI: Class id consist of numbers and characrers. The id can never start with a number. Example: "v_kw62ffss_xg0syjlvrokbxciv65a8y"

Related

Need help storing a value I get from a sqlite3 DB in a C++ variable

I'm having a 'Data.db' with 3 tables in it that store some kind of 'football data'.
One table is for 'teams' and another one is for 'players'. What I want to do now is to get the 'highest' ID from 'Teams' so I can assign newly generated players to that team.
(Every player has a _teamId)
I am able to print the ID I want by using this query and a callback_id that prints just the value I need.
Query:
string query = "SELECT COUNT(ID) FROM TEAMS;";
highest_id = (sqlite3_exec(db, query.c_str(), callback_id, 0, &messageError));
Callback:
int Team::callback_id(void* data, int count, char** values, char** columns) {
int id = atoi(values[1]);
std::cout << "callback_id says: " << id << endl;
return 0;
}
What do I have to do to store this id from my callback function in a variable that I can use later in my program? It seems like I can't just return it and I can't just assign the id value to my Team::_id as it gives me an error:
'invalid use of member ‘Team::_id’ in static member function'
Help would be much appreciated. I'm still trying to learn more about sqlite3 and the callback function in particular but I've wasted so much time on this problem that I don't know what else I could try.
You have a void* argument in sqlite3_exec which you currently set to 0. Instead, pass a pointer to an object there. This makes it possible to store what you like in that object in the callback.
One often passes a pointer to an object and call a non-static member function in that object to deal with the callback.
Example:
class Team {
public:
void func() {
std::string query = "SELECT COUNT(ID) FROM TEAMS;";
sqlite3_exec(db, query.c_str(), &Team::callback_id_proxy, this, &messageError);
// ^^^^
}
// a static callback proxy, casting `data` to a `Team*`
static int callback_id_proxy(void* data, int count, char** values, char** columns) {
return static_cast<Team*>(data)->callback_id(count, values, columns);
}
int callback_id(int count, char** values, char** columns) {
// here you can store what you like in your `Team` object
if(count > 0) highest_id = atoi(values[0]);
else highest_id = 0;
return 0;
}
int highest_id;
};
Note: You may want to use SELECT MAX(ID) FROM TEAMS; instead. Otherwise, you may get duplicates if you delete a team and then add a new team.
Another option is to make func and highest_id static too. I've renamed highest_id into number_of_teams here, because that's what it really is.
class Team {
public:
static int get_number_of_teams() {
std::string query = "SELECT COUNT(ID) FROM TEAMS;";
if(sqlite3_exec(db, query.c_str(),
&Team::callback_count,
nullptr, // static function, no "this" available
&messageError) == SQLITE_ABORT)
throw std::runtime_error("callback_id returned non-zero");
return number_of_teams;
}
static int callback_count(void* data, int count, char** values, char** columns) {
if(count > 0) {
number_of_teams = atoi(values[0]);
return 0;
}
return 1; // indicate error
}
private:
inline static int number_of_teams = -1;
};

How can I distinguish my characters in a vector? SFML 2.4.1

I am using push_back to add identical characters and for some reason these characters all have the same stats all the time.
Battle.cpp:
void Battle::InitalizeUser()
{
user.t_Pokemon.clear();
user.t_Item.clear();
user.currentPokemon_U = 0;
user.t_Pokemon.push_back(&uF1);
user.t_Pokemon.push_back(&uF1);
user.t_Pokemon.push_back(&uW3);
user.t_Pokemon.push_back(&uW3);
user.t_Pokemon.push_back(&uG5);
user.t_Pokemon.push_back(&uG5);
user.t_Item.push_back(&uI1);
user.t_Item.push_back(&uI2);
for (unsigned int i = 0; i < user.t_Pokemon.size(); i++)
{
user.t_Pokemon.at(i)->poke_health = 100;
user.t_Pokemon.at(i)->poke_isFainted = false;
}
assert(user.t_Pokemon.size() == 6);
assert(user.t_Item.size() == 2);
}
So if uF1 health is reduced all uF1 characters will suffer the same reduction, but I don't want all the others uF1 to suffer the reduction, I want to be able to distinguish them and have only one specific character affected.
How can I do that?
Pokemon.cpp:
void Pokemon::attackNormal(Pokemon * opponentPokemon)
{
opponentPokemon->poke_health = opponentPokemon->poke_health - 20;
opponentPokemon->changeIfFainted();
assert(opponentPokemon->poke_health <= 100 && opponentPokemon->poke_health >= 0);
}
Battle.h:
class Battle
{
private:
static Trainer user;
static Trainer ash;
static IntRect user_Rect;
static IntRect ash_Rect;
// User's pokemon
static Fire uF1;
static Water uW3;
static Grass uG5;
static Item uI1;
static Item uI2;
}
uF1 : Pokemon.h
class Fire : public Pokemon
{
public:
Fire();
Fire(string name);
virtual ~Fire();
void specialAttack(Pokemon * opponentPokemon);
void changeWeather();
void Draw(RenderWindow &window);
};
The problem is that your vector contains copies of a pointer what is not equivalent to having different copies of an actual object.
It seems that you are passing same pointers several times in order to achieve object copy which is not a case.
You should first declare all your objects and then add their addresses to the vector.
Additional note: you don't have to create a variable for every object you use. You could also create an array of objects.

an static function used in StringHashTable thread-safe?

I have got an StringHashTable class from
http://preshing.com/20110603/hash-table-performance-tests/
The following are parts of source :
class StringHashTable
{
static uint fnv1Hash(const char *key)
{
unsigned int hash = 2166136261ul;
for (const char *s = key; *s; s++)
hash = (16777619 * hash) ^ (*s);
return hash;
};
uint &operator[](const char *key)
{
uint hash = fnv1Hash(key) & (m_tableSize - 1);
Bucket *firstBucket = m_table + hash;
Bucket *b = firstBucket;
if (b->key)
{
do
{
if (strcmp(b->key, key) == 0)
return b->value;// Found existing bucket
b = b->next;
} while (b);
}
..........
}
}
Suppose that I have the global var :
StringHashTable hashtable(1024) ; //m_tableSize now 1024
And then the following is in main :
hashtable["0000"] = 0 ;
....
hashtable["9999"] = 9999 ;
After fill in all the data I need , thread 1 to n will get value according to key
while(1)
{
s = get(); //return string like "0000" ... "9999"
echo << hashtable[s.c_str()] << endl ;
}
I wonder if the StringHashTable would work fine in thread at first ,
because the function fnv1Hash is static , on second thought , there is no
static member data in this StringHashTable , so while thread1 is doing
hashtable["0000"] and thread2 is doing hashtable["9999"] at the very same time
both thread1 are calling fnv1Hash they will both get the right hash returned !!!
My question is : different thread call static uint fnv1Hash(const char *key) with
different key at the very same time still work fine ? In StringHashTable , fnv1Hash
is static for any reason ?!
The function fnv1Hash() doesn't access any non-local state other than the data pointed to by key. Assuming the content of the array key points to isn't written to concurrently, there is no threading issue. Of course, if another thread writes to the array pointed to by key, all bets are off.
Given that fnv1Hash() does access any of the object's data it doesn't need a this pointer. Thus, it is made static to indicate both to the human reader and the compiler that the objects won't be accessed implicitly. For the compiler the upshot is that it doesn't need to pass a this pointer.

Member variables of a object get overridden when creating another object in the object

I have a memory issue with a class of mine. The issue occurs when I create an object in a member function of a class. It is about the class below. I removed the member functions because they aren’t necessary:
class User
{
private:
bool locked;
bool active;
std::vector<City> * userCitys;
UserData userData;
Credentials credentials;
The problem occurs when I call this function:
int User::addCity(CityData cityData)
{
lockUserObject(); //Everything is fine here
City cityToAdd; //When this object is created, the memory of userCitys will get overridden
cityToAdd.activate();
userCitys->push_back(cityToAdd);
int cityID = userCitys->size() - 1;
userCitys->at(cityID).editCityData(cityData);
unlockUserObject();
return cityID;
}
In the first place I created userCitys on the stack. For test purpose I placed it on the Heap. The address of userCitys get overridden by some data. I can’t find the problem. the City is just a basic class:
Part of the header:
class City
{
private:
bool active;
Supplies supplies;
std::vector<Building> buildings;
std::vector<Company> companies;
std::vector<Share> shares;
std::vector<Troop> troops;
CityData cityData;
Constructor:
City::City()
{
active = false;
}
How is it possible that userCitys get overridden? This all happens on a single Thread so that can’t be a problem. I tried a lot of thing, but I can’t get it to work. What is the best approach to find the problem?
Edit:
Lock function:
void User::lockUserObject()
{
for( int i = 0; locked ; i++)
{
crossSleep(Settings::userLockSleepInterval);
if( i >= Settings::userLockMaxTimes )
Error::addError("User lock is over userLockMaxTimes",2);
}
locked = true;
}
I call the code here (Test function):
City * addCity(User * user)
{
Location location;
location.x = 0;
location.y = 1;
CityData citydata;
citydata.location = location;
citydata.villagers = 0;
citydata.cityName = "test city";
int cityID = user->addCity(citydata); //addCity is called here
City * city = user->cityAction(cityID);;
if( city == NULL)
Error::addError("Could not create a city",2);
return city;
}
The add user (Test code):
User * addUser()
{
UserData test;
test.name = "testtest";
Credentials testc("testtest",3);
//Create object user
int userID = UserControle::addUser(test,testc);
User * user = UserControle::UserAction(userID);
if( user == NULL)
Error::addError("Could not create a user",2);
return user;
}
My test function:
void testCode()
{
User * user = addUser();
City * city = addCity(user);
}
This function in called in main:
int main()
{
testCode();
return 0;
}
Here are UserAction and addUser in UserControle:
int UserControle::addUser(UserData userdata, Credentials credentials)
{
int insertID = -1;
for( int i = 0; i < (int)UserControle::users.size(); i++)
{
if( !UserControle::users.at(i).isActive() )
{
insertID = i;
break;
}
}
User userToInsert(userdata,credentials);
if( insertID != -1 )
{
UserControle::users.insert( UserControle::users.begin() + insertID,userToInsert);
return insertID;
}
else
{
UserControle::users.push_back(userToInsert);
return UserControle::users.size() - 1;
}
}
User* UserControle::UserAction(int userID) //check all indexes if greater then 0!
{
if( (int)UserControle::users.size() <= userID )
{
Error::addError("UserAction is out of range",3);
return NULL;
}
if( !UserControle::users.at(userID).isActive())
{
Error::addError("UserAction, the user is not active.",3);
return NULL;
}
return &UserControle::users[userID];
}
There's a few things you could try:
Remove code until the fault goes away. In other words, distill a minimal example from your code. I guess you'll then see the error yourself, otherwise post that small example program here and others will.
Don't use raw pointers. The question with those is always who owns what they point to. Use smart pointers instead, e.g. unique_ptr (C++11) or auto_ptr (C++98) for exclusive ownership.
If you have pointer members like "userCities", you need to think about what happens when copying instances of that class (you already wrote a proper destructor, or?). So, either prevent copying (make copy-constructor and assignment operator private and without implementing it) or implement them in a way that the vectors are properly cloned and not shared between different instances.
Don't use C-style casts. If those are necessary to get anything through the compiler, the code is probably broken.

Function has corrupt return value

I have a situation in Visual C++ 2008 that I have not seen before. I have a class with 4 STL objects (list and vector to be precise) and integers.
It has a method:
inline int id() { return m_id; }
The return value from this method is corrupt, and I have no idea why.
debugger screenshot http://img687.imageshack.us/img687/6728/returnvalue.png
I'd like to believe its a stack smash, but as far as I know, I have no buffer over-runs or allocation issues.
Some more observations
Here's something that puts me off. The debugger prints right values in the place mentioned // wrong ID.
m_header = new DnsHeader();
assert(_CrtCheckMemory());
if (m_header->init(bytes, size))
{
eprintf("0The header ID is %d\n", m_header->id()); // wrong ID!!!
inside m_header->init()
m_qdcount = ntohs(h->qdcount);
m_ancount = ntohs(h->ancount);
m_nscount = ntohs(h->nscount);
m_arcount = ntohs(h->arcount);
eprintf("The details are %d,%d,%d,%d\n", m_qdcount, m_ancount, m_nscount, m_arcount);
// copy the flags
// this doesn't work with a bitfield struct :(
// memcpy(&m_flags, bytes + 2, sizeof(m_flags));
//unpack_flags(bytes + 2); //TODO
m_init = true;
}
eprintf("Assigning an id of %d\n", m_id); // Correct ID.
return
m_header->id() is an inline function in the header file
inline int id() { return m_id; }
I don't really know how best to post the code snippets I have , but here's my best shot at it. Please do let me know if they are insufficient:
Class DnsHeader has an object m_header inside DnsPacket.
Main body:
DnsPacket *p ;
p = new DnsPacket(r);
assert (_CrtCheckMemory());
p->add_bytes(buf, r); // add bytes to a vector m_bytes inside DnsPacket
if (p->parse())
{
read_packet(sin, *p);
}
p->parse:
size_t size = m_bytes.size(); // m_bytes is a vector
unsigned char *bytes = new u_char[m_bytes.size()];
copy(m_bytes.begin(), m_bytes.end(), bytes);
m_header = new DnsHeader();
eprintf("m_header allocated at %x\n", m_header);
assert(_CrtCheckMemory());
if (m_header->init(bytes, size)) // just set the ID and a bunch of other ints here.
{
size_t pos = DnsHeader::SIZE; // const int
if (pos != size)
; // XXX perhaps generate a warning about extraneous data?
if (ok)
m_parsed = true;
}
else
{
m_parsed = false;
}
if (!ok) {
m_parsed = false;
}
return m_parsed;
}
read_packet:
DnsHeader& h = p.header();
eprintf("The header ID is %d\n", h.id()); // ID is wrong here
...
DnsHeader constructor:
m_id = -1;
m_qdcount = m_ancount = m_nscount = m_arcount = 0;
memset(&m_flags, 0, sizeof(m_flags)); // m_flags is a struct
m_flags.rd = 1;
p.header():
return *m_header;
m_header->init: (u_char* bytes, int size)
header_fmt *h = (header_fmt *)bytes;
m_id = ntohs(h->id);
eprintf("Assigning an id of %d/%d\n", ntohs(h->id), m_id); // ID is correct here
m_qdcount = ntohs(h->qdcount);
m_ancount = ntohs(h->ancount);
m_nscount = ntohs(h->nscount);
m_arcount = ntohs(h->arcount);
You seem to be using a pointer to an invalid class somehow. The return value shown is the value that VS usually uses to initialize memory with:
2^32 - 842150451 = 0xCDCDCDCD
You probably have not initialized the class that this function is a member of.
Without seeing more of the code in context.. it might be that the m_id is out of the scope you expect it to be in.
Reinstalled VC++. That fixed everything.
Thank you for your time and support everybody! :) Appreciate it!