I am writing a plugin for SA-MP, based on AMX and have occured an annoying problem. I am using a deque and a function to find & delete an element. (like this one below)
enum PARAM_TYPE {
PARAM_TYPE_CELL,
PARAM_TYPE_ARRAY,
PARAM_TYPE_STRING,
};
struct params_s {
enum PARAM_TYPE type;
struct params_s * next;
cell free;
cell numData;
cell arrayData[0];
};
struct timer_s {
AMX * amx;
int id, func, interval, repeat;
long long unsigned int trigger;
struct params_s * params;
};
std::deque<struct timer_s *> gTimers;
void DestroyTimer(struct timer_s * t) {
for (int i = 0; i != gTimers.size(); ++i) {
if (t == gTimers[i]) {
gTimers.erase(gTimers.begin() + i);
break;
}
}
}
Whenever I call DestroyTimer() I get this error:
Debug Assertion Failed!
Expression: deque subscript out of range
I can add elements, read and modify them, but I can't delete them.
Thank you.
You should use the erase remove idiom:
void DestroyTimer(struct timer_s * t)
{
gTimers.erase(remove(gTimers.begin(), gTimers.end(), t), gTimers.end());
}
Without looking at the actual error, the idiomatic way would be:
gTimers.erase(std::remove(gTimers.begin(), gTimers.end(), t),
gTimers.end());
This will be safer and faster than what you are doing now (catches
duplicates, no need to reallocate).
This is called Erase-Remove idiom.
For the actual debug assertion: Debugging iterators are a standard
extension and maybe broken in some cases.
NB: You want to call delete on the timer, if it is owned by the deque, to prevent leaking memory.
Related
Consider the following data structures and code.
struct Sentence {
std::string words;
int frequency;
Sentence(std::string words, int frequency) : words(words), frequency(frequency) {}
};
struct SentencePCompare {
bool operator() (const Sentence* lhs, const Sentence* rhs) const {
if (lhs->frequency != rhs->frequency) {
return lhs->frequency > rhs->frequency;
}
return lhs->words.compare(rhs->words) < 0;
}
};
std::set<Sentence*, SentencePCompare> sentencesByFrequency;
int main(){
Sentence* foo = new Sentence("foo", 1);
Sentence* bar = new Sentence("bar", 2);
sentencesByFrequency.insert(foo);
sentencesByFrequency.insert(bar);
for (Sentence* sp : sentencesByFrequency) {
std::cout << sp->words << std::endl;
}
foo->frequency = 5;
for (Sentence* sp : sentencesByFrequency) {
std::cout << sp->words << std::endl;
}
}
The output of the above code is the following.
bar
foo
bar
foo
As we might expect, when an object pointed to by the pointer in the set is updated, the set does not automatically re-evaluate the predicate, even though the predicate orders the pointers based on the objects they point at.
Is there a way to force the std::set to re-evaluate the predicates, so that the order is correct again?
No.
There's a reason why set only allows const access to its elements. If you sneak past that by using shallow-const pointers and custom predicates and then destroy the invariant by modifying the pointee in a way that affects ordering, you'll pay the price in the form of nasal demons.
Before C++17, you need to erase and insert again, which incurs a key copy plus node deallocation and allocation. After, you can extract the node, modify it, and reinsert it, which is free.
Guys I have a function like this (this is given and should not be modified).
void readData(int &ID, void*&data, bool &mybool) {
if(mybool)
{
std::string a = "bla";
std::string* ptrToString = &a;
data = ptrToString;
}
else
{
int b = 9;
int* ptrToint = &b;
data = ptrToint;
}
}
So I want to use this function in a loop and save the returned function parameters in a vector (for each iteration).
To do so, I wrote the following struct:
template<typename T>
struct dataStruct {
int id;
T** data; //I first has void** data, but would not be better to
// have the type? instead of converting myData back
// to void* ?
bool mybool;
};
my main.cpp then look like this:
int main()
{
void* myData = nullptr;
std::vector<dataStruct> vec; // this line also doesn't compile. it need the typename
bool bb = false;
for(int id = 1 ; id < 5; id++) {
if (id%2) { bb = true; }
readData(id, myData, bb); //after this line myData point to a string
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
}
}
Or is there a better way to do that without template? I used c++11 (I can't use c++14)
The function that you say cannot be modified, i.e. readData() is the one that should alert you!
It causes Undefined Behavior, since the pointers are set to local variables, which means that when the function terminates, then these pointers will be dangling pointers.
Let us leave aside the shenanigans of the readData function for now under the assumption that it was just for the sake of the example (and does not produce UB in your real use case).
You cannot directly store values with different (static) types in a std::vector. Notably, dataStruct<int> and dataStruct<std::string> are completely unrelated types, you cannot store them in the same vector as-is.
Your problem boils down to "I have data that is given to me in a type-unsafe manner and want to eventually get type-safe access to it". The solution to this is to create a data structure that your type-unsafe data is parsed into. For example, it seems that you inteded for your example data to have structure in the sense that there are pairs of int and std::string (note that your id%2 is not doing that because the else is missing and the bool is never set to false again, but I guess you wanted it to alternate).
So let's turn that bunch of void* into structured data:
std::pair<int, std::string> readPair(int pairIndex)
{
void* ptr;
std::pair<int, std::string> ret;
// Copying data here.
readData(2 * pairIndex + 1, ptr, false);
ret.first = *reinterpret_cast<int*>(ptr);
readData(2 * pairIndex + 2, ptr, true);
ret.second = *reinterpret_cast<std::string*>(ptr);
}
void main()
{
std::vector<std::pair<int, std::string>> parsedData;
parsedData.push_back(readPair(0));
parsedData.push_back(readPair(1));
}
Demo
(I removed the references from the readData() signature for brevity - you get the same effect by storing the temporary expressions in variables.)
Generally speaking: Whatever relation between id and the expected data type is should just be turned into the data structure - otherwise you can only reason about the type of your data entries when you know both the current ID and this relation, which is exactly something you should encapsulate in a data structure.
Your readData isn't a useful function. Any attempt at using what it produces gives undefined behavior.
Yes, it's possible to do roughly what you're asking for without a template. To do it meaningfully, you have a couple of choices. The "old school" way would be to store the data in a tagged union:
struct tagged_data {
enum { T_INT, T_STR } tag;
union {
int x;
char *y;
} data;
};
This lets you store either a string or an int, and you set the tag to tell you which one a particular tagged_data item contains. Then (crucially) when you store a string into it, you dynamically allocate the data it points at, so it will remain valid until you explicitly free the data.
Unfortunately, (at least if memory serves) C++11 doesn't support storing non-POD types in a union, so if you went this route, you'd have to use a char * as above, not an actual std::string.
One way to remove (most of) those limitations is to use an inheritance-based model:
class Data {
public:
virtual ~Data() { }
};
class StringData : public Data {
std::string content;
public:
StringData(std::string const &init) : content(init) {}
};
class IntData : public Data {
int content;
public:
IntData(std::string const &init) : content(init) {}
};
This is somewhat incomplete, but I think probably enough to give the general idea--you'd have an array (or vector) of pointers to the base class. To insert data, you'd create a StringData or IntData object (allocating it dynamically) and then store its address into the collection of Data *. When you need to get one back, you use dynamic_cast (among other things) to figure out which one it started as, and get back to that type safely. All somewhat ugly, but it does work.
Even with C++11, you can use a template-based solution. For example, Boost::variant, can do this job quite nicely. This will provide an overloaded constructor and value semantics, so you could do something like:
boost::variant<int, std::string> some_object("input string");
In other words, it's pretty what you'd get if you spent the time and effort necessary to finish the inheritance-based code outlined above--except that it's dramatically cleaner, since it gets rid of the requirement to store a pointer to the base class, use dynamic_cast to retrieve an object of the correct type, and so on. In short, it's the right solution to the problem (until/unless you can upgrade to a newer compiler, and use std::variant instead).
Apart from the problem in given code described in comments/replies.
I am trying to answer your question
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
Before that you need to modify vec definition as following
vector<dataStruct<void>> vec;
Now you can simple push element in vector
vec.push_back({id, &mydata, bb});
i have tried to modify your code so that it can work
#include<iostream>
#include<vector>
using namespace std;
template<typename T>
struct dataStruct
{
int id;
T** data;
bool mybool;
};
void readData(int &ID, void*& data, bool& mybool)
{
if (mybool)
{
data = new string("bla");
}
else
{
int b = 0;
data = &b;
}
}
int main ()
{
void* mydata = nullptr;
vector<dataStruct<void>> vec;
bool bb = false;
for (int id = 0; id < 5; id++)
{
if (id%2) bb = true;
readData(id, mydata, bb);
vec.push_back({id, &mydata, bb});
}
}
i want to implement a container, which contains maximal 20 Solutions.
If the container contains 20 Solutions any Solution that is added is only accepted if its
1) new_value < worst value in the Container
2) if new_value is already in the container (and 1) holds) then memcmp(new_assignment, assigment, assignment_size) != 0
Then the worst solutions is deleted. (Including the int array)
the assignment_size is for all Solutions the same.
struct Solution {
double value;
int *assignment;
int assigment_size;
};
What is the easiest way to implement this structure? Can you use some container of the STL?
Ok Thank you.
I hope it is correct. It tried to keep the list trough insert sorted so it is easier to check and then you can remove the last element.
struct Solution {
double power_peak;
int *assignment;
int size;
~Solution() { delete[] assignment; }
};
class Container {
list<Solution> Solutions;
uint max_size;
double worst_solution;
public:
Container (uint size)
{
max_size = size;
}
void addSolution(Solution s)
{
list<Solution>::iterator insert_position = Solutions.begin();
while (insert_position != Solutions.end() && (*insert_position).power_peak < s.power_peak)
{
if ((*insert_position).power_peak == s.power_peak && memcmp((*insert_position).assignment, s.assignment, s.size) == 0)
{
return;
}
++insert_position;
}
Solutions.insert(insert_position, s);
if (Solutions.size() > max_size)
{
Solutions.pop_back();
}
worst_solution = (*(--(Solutions.end())))->power_peak;
}
double getWorst()
{
return worst_solution;
}
};
Note: I assumed that your solution is described by the Solution structure you posted.
The easiest solution would be a std::array<Solution, 20>, wrapped in a custom container wrapper.
You should have something like this:
class SolutionsSequence
{
public:
// in a function to add a solution to the sequence, you should
// implement the criteria to decide which solution is worse
private:
std::array<Solution, 20> solutions_;
};
Other than that, you should not use memcpy in C++. Consider using std::copy, or std::copy_n instead.
Also consider using a std::vector for assignments, and then you can get rid of the assignment_size.
How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.
For some reason, I keep getting the following errors in ErrorHandler.h
why size function is missing arguments?
'std::list<_Ty>::size': function call missing argument list; use '&std::list<_Ty>::size' to create a pointer to member
'std::_List_iterator<_Mylist> std::list<_Ty>::erase(std::_List_const_iterator<_Mylist>,std::_List_const_iterator<_Mylist>)' : cannot convert parameter 1 from 'int' to 'std::_List_const_iterator<_Mylist>'
'std::_List_iterator<_Mylist> std::list<_Ty>::erase(std::_List_const_iterator<_Mylist>)' : cannot convert parameter 1 from 'int' to 'std::_List_const_iterator<_Mylist>'
// in errorhandler.h
class ErrorHandler{
std::list<unsigned int> m_ErrorList;
public:
ErrorHandler(){ }
~ErrorHandler(){ }
void ForceShutdown(){ free(&m_ErrorList); }
void Add(int errCode){ m_ErrorList.push_back(errCode); }
unsigned int GetLastError(){ if(m_ErrorList.size!=0)return m_ErrorList.back(); }
void Remove(int pos){ if(m_ErrorList.size!=0)m_ErrorList.erase(pos); }
void RemoveRange(int start,int end){ if(m_ErrorList.size!=0)m_ErrorList.erase(start,end); }
};
// in criticalsection.h
class CriticalSection{
long m_nLockCount;
long m_nThreadId;
typedef CRITICAL_SECTION cs;
cs m_tCS;
public:
CriticalSection(){
::InitializeCriticalSection(&m_tCS);
m_nLockCount = 0;
m_nThreadId = 0;
}
~CriticalSection(){ ::DeleteCriticalSection(&m_tCS); }
void Enter(){ ::EnterCriticalSection(&m_tCS); }
void Leave(){ ::LeaveCriticalSection(&m_tCS); }
void Try();
};
class LockSection{
CriticalSection* m_pCS;
ErrorHandler * m_pErrorHandler;
public:
LockSection(CriticalSection* pCS,ErrorHandler* pErrorHandler){
m_pCS = pCS;
m_pErrorHandler = pErrorHandler;
if(!m_pCS)m_pErrorHandler->Add(0x1AE1); // 0x1AE is code prefix for critical section header
if(m_pCS)m_pCS->Enter();
}
~LockSection(){
if(!m_pCS)m_pErrorHandler->Add(0x1AE2);
if(m_pCS)m_pCS->Leave();
}
};
http://www.cplusplus.com/reference/stl/list/pop_back/
Nope, pop_back does not return the last element. This is to prevent accidental errors. You have to get the last element explicitly via back(). This way is also faster if you want to pop several without reading them. This also applies to all the other Standard C++ Library containers.
Judging by your warnings, it looks like you're also having trouble deleting. For Lists it can be tricky:
void Remove(int pos){
std::list<unsigned int>::const_iterator iter = m_ErrorList.begin();
//no need to check the size, advance will throw an exception if pos is invalid
std::advance(iter, pos);
m_ErrorList.erase(iter);
}
You use the list methods badly:
if(m_ErrorList.size!=0)
size is a method, so you need to call it (with parentheses):
if(m_ErrorList.size()!=0)
Note that size is slow for list; you may want to implement GetLastError like this:
unsigned int GetLastError(){ if(!m_ErrorList.empty())return m_ErrorList.back(); }
m_ErrorList.erase(pos);
erase takes an iterator, not an integer. Therefore, you'd better use
std::list::iterator it=m_ErrorList.begin();
std::advance(it, pos);
m_ErrorList.erase(it);
note that this isn't a particularly efficient way, either.
BTW, check that you need list; a vector might serve you better.