Valgrind reports "Invalid free() / delete / delete[]" - c++

I'm not sure what could be causing this.
==18270== Invalid free() / delete / delete[]
==18270== at 0x400576A: operator delete(void*) (vg_replace_malloc.c:342)
==18270== by 0x80537F7: LCD::LCDControl::~LCDControl() (LCDControl.cpp:23)
==18270== by 0x806C055: main (Main.cpp:22)
==18270== Address 0x59e92c4 is 388 bytes inside a block of size 712 alloc'd
==18270== at 0x40068AD: operator new(unsigned int) (vg_replace_malloc.c:224)
==18270== by 0x8078033: LCD::DrvCrystalfontz::Get(std::string, LCD::LCDControl*, Json::Value*, std::string) (DrvCrystalfontz.cpp:652)
==18270== by 0x8053F50: LCD::LCDControl::ConfigSetup() (LCDControl.cpp:71)
==18270== by 0x80539F7: LCD::LCDControl::Start(int, char**) (LCDControl.cpp:31)
==18270== by 0x806C025: main (Main.cpp:21)
Here's LCDControl's destructor where delete is called.
LCDControl::~LCDControl() {
Shutdown();
for(std::vector<std::string>::iterator it = display_keys_.begin();
it != display_keys_.end(); it++) {
Error("Deleting %s %p", (*it).c_str(), devices_text_[*it]);
if(devices_text_.find(*it) != devices_text_.end() && devices_text_[*it])
delete devices_text_[*it]; // line 23
}
//delete app_;
}
Here's Crystalfontz::Get()
switch(m->GetProtocol()) {
case 1:
return new Protocol1(name, m, v, config);
break;
case 2:
return new Protocol2(name, m, v, config); // line 652
break;
case 3:
return new Protocol3(name, m, v, config, scab);
break;
default:
Error("Internal error. Model has bad protocol: <%s>",
m->GetName().c_str());
break;
devices_text_:
std::map<std::string, Generic <LCDText>*> devices_text_;
LCDControl::ConfigSetup(),
void LCDControl::ConfigSetup() {
if(!CFG_Get_Root()) return;
Json::Value::Members keys = CFG_Get_Root()->getMemberNames();
for(std::vector<std::string>::iterator it = keys.begin(); it != keys.end(); it++ ) {
if(it->find("display_", 0) != std::string::npos) {
Json::Value *display = CFG_Fetch_Raw(CFG_Get_Root(), it->c_str());
Json::Value *driver = CFG_Fetch_Raw(display, "driver");
if(!driver) {
Error("CFG: Must specify driver <%s>", it->c_str());
continue;
}
Json::Value *rows = CFG_Fetch_Raw(display, "rows", new Json::Value(-1));
/*if(!rows->isNumeric() || rows->asInt() == -1) {
Error("Display <%s> requires number of rows to initialize.", it->c_str());
delete display;
delete driver;
continue;
}*/
Json::Value *cols = CFG_Fetch_Raw(display, "cols", new Json::Value(-1));
/*if(!cols->isNumeric() || rows->asInt() == -1) {
Error("Display <%s> requires number of columns to initialize.", it->c_str());
delete display;
delete driver;
delete rows;
continue;
}*/
Json::Value *model = CFG_Fetch_Raw(display, "model");
if(driver->asString() == "crystalfontz") {
if(model) {
devices_text_[*it] = DrvCrystalfontz::Get(*it, this,
CFG_Get_Root(), model->asString()); // line 71
} else {
Error("Device <%s> requires a model.", it->c_str());
delete display;
delete driver;
delete rows;
delete cols;
continue;
}
} else if(driver->asString() == "qt") {
devices_text_[*it] = new DrvQt(*it, this, CFG_Get_Root(),
rows->asInt(), cols->asInt());
} else if(driver->asString() == "pertelian") {
//devices_text_[*it] = new DrvPertelian(this, CFG_Get_Root(), rows->asInt(), cols->asInt());
} else
continue;
if(model) delete model;
delete display;
delete driver;
delete rows;
delete cols;
}
}
for(std::map<std::string, Generic<LCDText> *>::iterator it =
devices_text_.begin(); it != devices_text_.end(); it++) {
display_keys_.push_back(it->first);
Error("Starting <%s> %p", it->first.c_str(), it->second);
Generic<LCDText> *device = it->second;
device->CFGSetup(it->first);
device->Connect();
device->SetupDevice();
device->BuildLayouts();
device->StartLayout();
}
}

I'm guessing that Protocol1 et al. are subclasses of some SuperProtocol?
devices_text_[*it] goes through assuming that it contains something of type SuperProtocol, so delete devices_text_[*it] calls SuperProtocol::~ SuperProtocol().
However, what you really want to call is Protocol1::~Protocol1() if you're destructing a Protocol1; this only works if you mark SuperProtocol::~ SuperProtocol() as virtual.

Instead of going through the keys, finding it in the map, then deleting it, why not just iterate through the map deleting as you go? I'd make a functor and use for_each (this isn't a guideline or anything, just my opinion),
typedef Generic<LCDText> GenericLCDText;
typedef std::map<std::string, GenericLCDText*> GenericLCDTextMap;
typedef GenericLCDTextMap::value_type GenericLCDTextPair;
struct device_text_deleter : std::unary_function<const GenericLCDTextPair&, void>
{
void operator()(const GenericLCDTextPair& pPair)
{
Error("Deleting %s %p", pPair.first.c_str(), pPair.second);
delete pPair.second;
}
}
std::for_each(devices_text_.begin(), devices_text_.end(), device_text_deleter());
_devices.text_.clear(); // optional, removes the deleted pointers. unnecessary
// if this is run in the destructor, since map goes away
// shortly after
That said, you're code would be improved by the following:
// typedef's for readability (would be in header, maybe private)
typedef std::vector<std::string> StringVector;
typedef Generic<LCDText> GenericLCDText;
typedef std::map<std::string, GenericLCDText*> GenericLCDTextMap;
for(StringVector::iterator it = display_keys_.begin();
it != display_keys_.end(); it++)
{
// find first! otherwise you're looking up the pair every time
GenericLCDTextMap::iterator pair = devices_text_.find(*it);
if (p != devices_text_.end())
{
// operator-> is overloaded for iterators but might as well use
// the pair now.
Error("Deleting %s %p", pair->first.c_str(), pair->second);
// no need to check for null, delete null is a-ok
delete pair->second;
}
}
Hopefully this will make it easier to spot the errors. Make sure any base classes you use have virtual destructors.
Check you haven't added a string in the vector twice (this would be "fixed" buy just iterating through the map, though you'll want to find out why duplicates exist in the first place), etc.
I've never tried this before, but maybe add a double delete macro thing (totally untested):
#define DOUBLE_DELETE_GAURD static bool x = false; assert(x == false); x = true;
Then just add it to your destructor. If you double delete, and the static bool is still around, the assertion will fail. This is completely in undefined la-la land, though.

Could it be that
display_keys_ contains the same string more than once and
this string has an associated Generic <LCDText>* in devices_text_?
In this case the pointer would be given to delete twice and this isn't legal...

Related

std::list and garbage Collection algorithm

I have a server that puts 2 players together on request and starts a game Game in a new thread.
struct GInfo {Game* game; std::thread* g_thread};
while (true) {
players_pair = matchPlayers();
Game* game = new Game(players_pair);
std::thread* game_T = new std::thread(&Game::start, game);
GInfo ginfo = {game, game_T}
_actives.push_back(ginfo); // std::list
}
I am writing a "Garbage Collector", that runs in another thread, to clean the memory from terminated games.
void garbageCollector() {
while (true) {
for (std::list<Ginfo>::iterator it = _actives.begin(); it != _actives.end(); ++it) {
if (! it->game->isActive()) {
delete it->game; it->game = nullptr;
it->g_thread->join();
delete it->g_thread; it->g_thread = nullptr;
_actives.erase(it);
}
}
sleep(2);
}
}
This generates a segfault, I suspect it is because of the _active.erase(it) being in the iteration loop.
For troubleshooting, I made _actives an std::vector (instead of std::list) and applied the same algorithm but using indexes instead of iterators, it works fine.
Is there a way around this?
Is the algorithm, data structure used fine? Any better way to do the garbage collection?
Help is appreciated!
If you have a look at the documentation for the erase method it returns an iterator to the element after the one that was removed.
The way to use that is to assign the returned value to your iterator like so.
for (std::list<Ginfo>::iterator it = _actives.begin(); it != _actives.end();) {
if (! it->game->isActive()) {
delete it->game; it->game = nullptr;
it->g_thread->join();
delete it->g_thread; it->g_thread = nullptr;
it = _actives.erase(it);
}
else {
++it;
}
}
Since picking up the return value from erase advances the iterator to the next element, we have to make sure not to increment the iterator when that happens.
On an unrelated note, variable names starting with underscore is generally reserved for the internals of the compiler and should be avoided in your own code.
Any better way to do the garbage collection?
Yes, don't use new,delete or dynamic memory alltogether:
struct Players{};
struct Game{
Game(Players&& players){}
};
struct GInfo {
GInfo(Players&& players_pair):
game(std::move(players_pair)),g_thread(&Game::start, game){}
Game game;
std::thread g_thread;
};
std::list<GInfo> _actives;
void someLoop()
{
while (true) {
GInfo& ginfo = _actives.emplace_back(matchPlayers());
}
}
void garbageCollector() {
while (true) {
//Since C++20
//_active.remove_if([](GInfo& i){ return !i.game.isActive();});
//Until C++20
auto IT =std::remove_if(_actives.begin(),_actives.end(),
[](GInfo& i){ return !i.game.isActive();});
_active.erase(IT,_active.end());
//
sleep(2);
}
}
There might be a few typos, but that's the idea.

How to erase an object of a list containing pointers to a struct?

I have a struct called Node with 3 members, one of which acts as an id for the objects:
struct Node {
string mem1;
string mem2;
int id;
}
And a list containing pointers to Node objects:
list<Node*> g_node;
The problem comes when trying to erase a specific object from that list (localized by the id). I have this code but doesn't work:
list<Node>::iterator it = g_node.begin();
while (it != g_node.end()){
if (it->id == iden)
{
g_node->erase(it);
}
}
} else if (iden != 0) {
"iden" is the id of an object to be deleted, and is input by the user.
What's going wrong?
Why not use std::list::remove_if?
g_node.remove_if([iden](const Node *n){return n->id == iden;});
Note that this will not delete the Node object (neither does your original code). With containers holding pointers, you might want to consider smart pointers.
remove_if is a great idea, but if you want to have a function that you can easily reuse and customize at your will, you can do it like this:
bool remove_from_list(int id, list<Node*> &g_node)
{
auto it = g_node.begin();
while (it != g_node.end())
{
if ((*it)->id == id)
{
// free memory... if you allocated those pointers
delete (*it);
g_node.erase(it);
return true;
}
else
it++;
}
return false;
}
list<Node*> g_node;
g_node.push_back(new Node { "a", "b", 5 });
g_node.push_back(new Node { "ee", "77", 6 });
remove_from_list(5, g_node);

Recursive insert of a chain into memory fails

This meight be a long question but i hope someone can help me figuring out whats going wrong.
I am inserting a JSON Object into already allocated Memory with my own Datatype which basically holds a Union with Data and a ptrdiff_t to the next Datatype in 8bit steps.
template <typename T>
class BaseType
{
public:
BaseType();
explicit BaseType(T& t);
explicit BaseType(const T& t);
~BaseType();
inline void setNext(const ptrdiff_t& next);
inline std::ptrdiff_t getNext();
inline void setData(T& t);
inline void setData(const T& t);
inline T getData() const;
protected:
union DataUnion
{
T data;
::std::ptrdiff_t size;
DataUnion()
{
memset(this, 0, sizeof(DataUnion));
} //init with 0
explicit DataUnion(T& t);
explicit DataUnion(const T& t);
} m_data;
long long m_next;
};
The implementation is streight so nothing special happes there just setting/getting the values of the definition. (i'll skip the impl. here)
So here starts the code where something goes wrong:
std::pair<void*, void*> Page::insertObject(const rapidjson::GenericValue<rapidjson::UTF8<>>& value,
BaseType<size_t>* last)
{
//return ptr to the first element
void* l_ret = nullptr;
//prev element ptr
BaseType<size_t>* l_prev = last;
//position pointer
void* l_pos = nullptr;
//get the members
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it)
{
switch (it->value.GetType())
{
case rapidjson::kNullType:
LOG_WARN << "null type: " << it->name.GetString();
continue;
case rapidjson::kFalseType:
case rapidjson::kTrueType:
{
l_pos = find(sizeof(BaseType<bool>));
void* l_new = new (l_pos) BaseType<bool>(it->value.GetBool());
if (l_prev != nullptr)
l_prev->setNext(dist(l_prev, l_new));
}
break;
case rapidjson::kObjectType:
{
//pos for the obj id
//and insert the ID of the obj
l_pos = find(sizeof(BaseType<size_t>));
std::string name = it->name.GetString();
void* l_new = new (l_pos) BaseType<size_t>(common::FNVHash()(name));
if (l_prev != nullptr)
l_prev->setNext(dist(l_prev, l_new));
//TODO something strange happens here!
// pass the objid Object to the insertobj!
// now recursive insert the obj
// the second contains the last element inserted
// l_pos current contains the last inserted element and get set to the
// last element of the obj we insert
l_pos = (insertObject(it->value, reinterpret_cast<BaseType<size_t>*>(l_new)).second);
}
break;
case rapidjson::kArrayType:
{//skip this at the moment till the bug is fixed
}
break;
case rapidjson::kStringType:
{
// find pos where the string fits
// somehow we get here sometimes and it does not fit!
// which cant be since we lock the whole page
l_pos = find(sizeof(StringType) + strlen(it->value.GetString()));
//add the String Type at the pos of the FreeType
auto* l_new = new (l_pos) StringType(it->value.GetString());
if (l_prev != nullptr)
l_prev->setNext(dist(l_prev, l_new));
}
break;
case rapidjson::kNumberType:
{
//doesnt matter since long long and double are equal on x64
//find pos where the string fits
l_pos = find(sizeof(BaseType<long long>));
void* l_new;
if (it->value.IsInt())
{
//insert INT
l_new = new (l_pos) BaseType<long long>(it->value.GetInt64());
}
else
{
//INSERT DOUBLE
l_new = new (l_pos) BaseType<double>(it->value.GetDouble());
}
if (l_prev != nullptr)
l_prev->setNext(dist(l_prev, l_new));
}
break;
default:
LOG_WARN << "Unknown member Type: " << it->name.GetString() << ":" << it->value.GetType();
continue;
}
//so first element is set now, store it to return it.
if(l_ret == nullptr)
{
l_ret = l_pos;
}
//prev is the l_pos now so cast it to this;
l_prev = reinterpret_cast<BaseType<size_t>*>(l_pos);
}
//if we get here its in!
return{ l_ret, l_pos };
}
I am starting to insert like this:
auto firstElementPos = insertObject(value.MemberBegin()->value, nullptr).first;
While value.MemberBegin()->value is Object to be inserted and ->name holds the Name of the object. In the case below its Person and everything between {}.
The problem is, if i insert a JSON Object which has one Object inside like so:
"Person":
{
"age":25,
"double": 23.23,
"boolean": true,
"double2": 23.23,
"firstInnerObj":{
"innerDoub": 12.12
}
}
It works properly and i can reproduce the Object. But if i have more inner objects like so:
"Person":
{
"age":25,
"double": 23.23,
"boolean": true,
"double2": 23.23,
"firstInnerObj":{
"innerDoub": 12.12
},
"secondInnerObj":{
"secInnerDoub": 12.12
}
}
It fails and i lose data so i think that my recursion goes wrong but i dont see why. If you need any more informations let me know. Meight take a look here and the client here.
The test.json need to contain a json object like above. And the find only need to contain {"oid__":2} to get the second object that was inserted.
I could track the issue down to the Point where i recreate the Object recursively in the code. Some of the Nextpointers seem to be incorrect:
void* Page::buildObject(const size_t& hash, void* start, rapidjson::Value& l_obj,
rapidjson::MemoryPoolAllocator<>& aloc)
{
//get the meta information of the object type
//to build it
auto& l_metaIdx = meta::MetaIndex::getInstance();
//get the meta dataset
auto& l_meta = l_metaIdx[hash];
//now we are already in an object here with l_obj!
auto l_ptr = start;
for (auto it = l_meta->begin(); it != l_meta->end(); ++it)
{
//create the name value
rapidjson::Value l_name(it->name.c_str(), it->name.length(), aloc);
//create the value we are going to add
rapidjson::Value l_value;
//now start building it up again
switch (it->type)
{
case meta::OBJECT:
{
auto l_data = static_cast<BaseType<size_t>*>(l_ptr);
//get the hash to optain the metadata
auto l_hash = l_data->getData();
//set to object and create the inner object
l_value.SetObject();
//get the start pointer which is the "next" element
//and call recursive
l_ptr = static_cast<BaseType<size_t>*>(buildObject(l_hash,
(reinterpret_cast<char*>(l_data) + l_data->getNext()), l_value, aloc));
}
break;
case meta::ARRAY:
{
l_value.SetArray();
auto l_data = static_cast<ArrayType*>(l_ptr);
//get the hash to optain the metadata
auto l_size = l_data->size();
l_ptr = buildArray(l_size, static_cast<char*>(l_ptr) + l_data->getNext(), l_value, aloc);
}
break;
case meta::INT:
{
//create the data
auto l_data = static_cast<BaseType<long long>*>(l_ptr);
//with length attribute it's faster ;)
l_value = l_data->getData();
}
break;
case meta::DOUBLE:
{
//create the data
auto l_data = static_cast<BaseType<double>*>(l_ptr);
//with length attribute it's faster ;)
l_value = l_data->getData();
}
break;
case meta::STRING:
{
//create the data
auto l_data = static_cast<StringType*>(l_ptr);
//with length attribute it's faster
l_value.SetString(l_data->getString()->c_str(), l_data->getString()->length(), aloc);
}
break;
case meta::BOOL:
{
//create the data
auto l_data = static_cast<BaseType<bool>*>(l_ptr);
l_value = l_data->getData();
}
break;
default:
break;
}
l_obj.AddMember(l_name, l_value, aloc);
//update the lptr
l_ptr = static_cast<char*>(l_ptr) + static_cast<BaseType<size_t>*>(l_ptr)->getNext();
}
//return the l_ptr which current shows to the next lement. //see line above
return l_ptr;
}
After houers and houres of debugging i found the small issue which causes this. The method which builds up the Object after it was inserted returns a pointer to the actuall last element->next which was inserted and after the switch case i did call the ->next again which causes a loss of data because it scipped one element in the single chained list.
The Fix to this is to put the line
l_ptr = static_cast<char*>(l_ptr) + static_cast<BaseType<size_t>*>(l_ptr)->getNext();
Only into the switch cases where it is not an Object or Array. Fix Commit This actually also gave me the fix for an Issue with inserting Array.
Of cause the real issue could not know someone here who did not took a deep look into the code but i still want to show the fix here. Thanks to #sehe who helped alot with figuring out whats going wrong here.

Exc access error

Very new to C++ and having problems returning a vector. I put a breakpoint and the array is correct (populated with all the objects I would expect from the query). But when it returns I get an error:
EXC_BAD_ACCESS
on line m_pComponentContainer->removeAll();
from CCNode.cpp
Which is strange since this is a base class (does NOT inherit from any kind of CC object) although I am extensively using the Cocos2dx framework, its not included in this class.
Im fairly sure this is because something is being deallocated. However like I said Im very new to C++ and not really sure where the problem is. I was hoping to get a little further in development before I had to start worrying about memory management.
int numberOfCards = DatabaseHelper::getNumberOfCards();
//cant be zero
assert(numberOfCards);
std::vector<CardSlot> returnArray(numberOfCards);
sqlite3_stmt * statement;
if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)
{
const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";
if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
CardSlot *aCard;
const char* cardID = (const char*)sqlite3_column_text(statement, 0);
const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);
if (cardID != NULL) {
std::string imageName = ".png";
imageName = cardID + imageName;
aCard = (CardSlot *)CardSlot::spriteWithFile(imageName.c_str());
}
if (cardID != NULL) {
aCard->cardID = std::string(cardID);
cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
}
if (cardHP != NULL) {
aCard->cardHP = std::string(cardHP);
cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
}
if (cardMP != NULL) {
aCard->cardMP = std::string(cardMP);
cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
}
if (cardAbility != NULL) {
aCard->cardAbility = std::string(cardAbility);
cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
}
numberOfCards--;
returnArray[numberOfCards] = *aCard;
}
sqlite3_finalize(statement);
}
sqlite3_close(this->cardWarsDB);
return returnArray;
}
Here is a screenshot of the stack trace. I was just looking at it, and it seems that it is the CardSlot objects are the culprits.
But still dont know how to "retain" them, but Ill look at some Cocos documentation.
NOTE1
It looks like your CardSlot is not safe to copy. You copy CardSlots in at least two places:
aCard = * CardSlot::spriteWithFile(imageName.c_str()); (also a memory leak assuming spriteWithFile returns CardSlot *; the "temporary" is not destructed)
returnArray[numberOfCards] = aCard;
From what I can tell, you are probably keeping a CCSprite pointer in CardSlot and destroying it (with delete) in your CardSlot destructor. However, this pointer gets destroyed multiple times because of the copies, which causes your crash.
You need to redesign your class so it can either be safely copied, or refactor your code so that you make no copies (e.g. by using a vector<shared_ptr<CardSlot> > to hold pointers to the instances).
I have edited the code to use more pointers rather then passing around and filling my array with objects. However I think a big thing that helped fix it was using cocos2d::CCArray instead of a std::vector. Most of my classes are children of Cocos2d classes (CCSprites, and CCLayers) and so using its own array data type makes sense.
cocos2d::CCArray DatabaseHelper::getAllCards()
{
int numberOfCards = DatabaseHelper::getNumberOfCards();
//cant be zero
assert(numberOfCards);
cocos2d::CCArray returnArray(numberOfCards);
sqlite3_stmt * statement;
if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)
{
const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";
if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
CardSlot* aCard;
const char* cardID = (const char*)sqlite3_column_text(statement, 0);
const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);
if (cardID != NULL) {
std::string imageName = ".png";
imageName = cardID + imageName;
aCard = CardSlot::spriteWithFile(imageName.c_str());
}
if (cardID != NULL) {
aCard->cardID = std::string(cardID);
cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
}
if (cardHP != NULL) {
aCard->cardHP = std::string(cardHP);
cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
}
if (cardMP != NULL) {
aCard->cardMP = std::string(cardMP);
cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
}
if (cardAbility != NULL) {
aCard->cardAbility = std::string(cardAbility);
cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
}
numberOfCards--;
returnArray.addObject(aCard);
}
sqlite3_finalize(statement);
}
sqlite3_close(this->cardWarsDB);
return returnArray;
}
//incase sql fails, close db and created a "FAILED" card
sqlite3_close(this->cardWarsDB);
cocos2d::CCLog("DB returned error: cant open char catagories file");
cocos2d::CCArray failedReturnArray(1);
CardSlot * aCard;
aCard->cardID = std::string("FAILED");
aCard->cardHP = std::string("FAILED");
aCard->cardMP = std::string("FAILED");
aCard->cardAbility = std::string("FAILED");
failedReturnArray.addObject(aCard);
return failedReturnArray;
}
Also in case anyone cares here is CardSlot (not much to it, only built the constructor at this time):
CardSlot * CardSlot::spriteWithFile(const char *pszFileName)
{
CCLOG("CardSlot::spriteWithFile");
CardSlot * aCard = new CardSlot();
if (aCard && aCard->initWithFile(pszFileName))
{
aCard->cardID = pszFileName;
aCard->scheduleUpdate();
aCard->autorelease();
return aCard;
}
CC_SAFE_DELETE(aCard);
return NULL;
}
The only thing Im concerned about is that I think my CCArray should be a pointer. But its working now and learning all the memory management "tricks of the trade" will come in time, the more I work with C++
Thanks #nneonneo for all the help Im sure your fix would have worked and I tried but no matter what I did couldnt get the vector to work. I 1up'd you as much as I could but really this is the "Answer" I implemented.
The returnArray is declared local to the function, it will be deallocated when the function returns. You would need to either declare it as static or move the declaration to outside the function.

Opencl for C++, buffer.release() is protected member

Im learning how to make simple opencl programs in C++ using Amd's app sdk and Khronos's header files for Opencl 1.2. I used the below example and it is working. But when I try to .release() the buffers in the end, I get an error message from compiler saying "cannot access protected member".
int problemSize=1024;
const char * kernelDerlenecek =
"__kernel void Toplam(__global float * v1, __global float * v2)"
"{"
" int i = get_global_id(0);"
" v2[i]=v1[i];"
"}";
cl::Context altYapi(CL_DEVICE_TYPE_GPU);
cl::Program::Sources kaynaklar;
kaynaklar.push_back(std::make_pair(kernelDerlenecek,strlen(kernelDerlenecek)));
cl::Program program(altYapi,kaynaklar);
std::vector<cl::Device> aygitlar=altYapi.getInfo<CL_CONTEXT_DEVICES>();
program.build(aygitlar);
cl::Kernel kernel(program,"Toplam");
cl::CommandQueue cmdQ(altYapi,aygitlar[0]);
std::vector<cl_float> input;
std::generate_n (std::back_inserter ( input ) , problemSize , rand) ;
cl::Buffer inputBuffer(altYapi, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, sizeof(cl_float) * input.size(), &input[0]);
cl::Buffer outputBuffer(altYapi ,CL_MEM_WRITE_ONLY , sizeof(cl_float )* input.size()) ;
kernel.setArg(0,inputBuffer);
kernel.setArg(1,outputBuffer);
cl::NDRange Global(1024);
cl::NDRange Local(64);
cmdQ.enqueueNDRangeKernel(kernel,cl::NullRange,Global,Local);
float * output = (float *)malloc(1024*sizeof(float));
cmdQ.enqueueReadBuffer(outputBuffer,CL_TRUE,0,1024 * sizeof(cl_float),output);
for(int i=0;i<1024;i++)
{
printf(" %f \n ",output[i]);
}
free(output);
//inputBuffer.release(); <----- this is not accessible!!!
//inputBuffer.~Wrapper() <----- invalid destructor name!!!
So I searched for the structure of wrapper class of buffer and found that:
class Wrapper
{
public:
typedef T cl_type;
protected:
cl_type object_;
public:
Wrapper() : object_(NULL) { }
~Wrapper()
{
if (object_ != NULL) { release(); } //This is releasing, should I destroy myself?
}
Wrapper(const Wrapper<cl_type>& rhs)
{
object_ = rhs.object_;
if (object_ != NULL) { retain(); }
}
Wrapper<cl_type>& operator = (const Wrapper<cl_type>& rhs)
{
if (object_ != NULL) { release(); }
object_ = rhs.object_;
if (object_ != NULL) { retain(); }
return *this;
}
cl_type operator ()() const { return object_; }
cl_type& operator ()() { return object_; }
protected:
cl_int retain() const
{
return ReferenceHandler<cl_type>::retain(object_);
}
cl_int release() const //<---yes, its true that I cannot access. Who can?
{
return ReferenceHandler<cl_type>::release(object_);
}
};
Question: How can I release the memory dedicated to buffers? Even the ~Wrapper() nor ~Memory() does not work because error says "invalid destructor name".
Maybe it destroys itself when function exits? This is in a dll and from a C# wrapper, its being called repeatedly so I need it to be released only when needed. Thanks.
It is good that the class Buffer handles itself the allocation and destruction.
If you want to allocate and destroy it manually just do this:
vector<cl::Buffer> mybuffer;
Then simply to create a memory zone:
mybuffer.push_back(cl::Buffer(/*constructor parameters*/));
To destroy it:
mybuffer.clear();
And the good thing is that if you forget to delete it, it will delete it automatically.
You can wrap it in a pointer, preferably a smart one, like so:
std::unique_ptr<cl::Buffer> myBuffer(new cl::Buffer(...));
Then if you want to manually delete the data, or replace the data with something else:
myBuffer.reset(new cl::Buffer(...));
This will delete the old data and replace it with the new one, keep the "reset()" blank if you want to just delete the old data.