After updating my files to boost_1.59.0 i get an ambyguous error. I can't understand what's wrong because in boost 1.43 all work's fine.
This is my boost declaration and my function.
boost::unordered_map<VID, size_t>::iterator iterTargetMap = rSkillUseInfo.TargetVIDMap.find(TargetVID);
if (rSkillUseInfo.TargetVIDMap.end() != iterTargetMap)
{
size_t MaxAttackCountPerTarget = 1;
switch (SkillID)
{
case SKILL_SAMYEON:
case SKILL_CHARYUN:
MaxAttackCountPerTarget = 3;
break;
}
if (iterTargetMap->second >= MaxAttackCountPerTarget)
{
sys_log(0, "SkillHack: Too Many Hit count from SkillID(%u) count(%u)", SkillID, iterTargetMap->second);
return false;
}
iterTargetMap->second++;
}
else
{
rSkillUseInfo.TargetVIDMap.insert( std::make_pair(TargetVID, 1) );
}
I also tried with auto in c++11
auto iterator iterTargetMap = rSkillUseInfo.TargetVIDMap.find(TargetVID);
Here is my error log gcc49 http://pastebin.com/p1KLqs9H
I can't write here the error is too big.
I'am stucked on this error for 4 days. :(
Here is vid.h
class VID
{
public:
VID() : m_id(0), m_crc(0)
{
}
VID(DWORD id, DWORD crc)
{
m_id = id;
m_crc = crc;
}
VID(const VID &rvid)
{
*this = rvid;
}
const VID & operator = (const VID & rhs)
{
m_id = rhs.m_id;
m_crc = rhs.m_crc;
return *this;
}
bool operator == (const VID & rhs) const
{
return (m_id == rhs.m_id) && (m_crc == rhs.m_crc);
}
bool operator != (const VID & rhs) const
{
return !(*this == rhs);
}
operator DWORD() const
{
return m_id;
}
void Reset()
{
m_id = 0, m_crc = 0;
}
private:
DWORD m_id;
DWORD m_crc;
};
By looking at the error, it looks like you have to define a hash function for the type VID to be able to use it as a key in a map.
Standard hash functions are already defined in the STL for basic types, but you have to define for yourself a specific one for your domain types.
Usually, it's enough to do something like this:
namespace std {
template<> struct hash<VID> {
using argument_type = VID;
using result_type = std::size_t;
result_type operator()(argument_type const& vid) const {
// what to put here depends on the type of VID
// and how you want to create the hash
}
};
}
The difficulties are usually in understanding how to create the hash. In my experience, for user defined classes, I've ever used the standard specialization with some data members, the most significant ones.
In your case, as an example, you could cast the DWORDs to a couple of unsigned ints and use them to get the hash by using std::hash<unsigned int> (I'm assuming that that's the DWORD from the Windows API, that is a 32 bit unsigned integer as far as I remember).
As already said in the comments, see here for further details.
Related
I'm testing std::set with a custom comparator. But I see the same object getting inserted twice.
Following is the object class:
class Info
{
public:
Info(string n, string oN, int dom):
name(n),
objName(oN),
domain(dom)
{}
void setName(std::string n) { name = n;}
void setObjName(std::string n) { objName = n;}
void setDomain(int dom) { domain = dom; }
std::string getName() const { return name;}
std::string getObjName() const { return objName;}
int getDomain() const { return domain;}
private:
std::string name;
std::string objName;
int domain;
};
Following is my custom comparator:
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName()) || (lhs.getObjName() < rhs.getObjName()) || (lhs.getDomain() < rhs.getDomain()) ){
return true;
}
return false;
}
};
Following is the usage:
Info rst1("rst1", "rstObj1", 1);
Info rst2("rst2", "rstObj2", 2);
Info rst3("rst1", "rstObj3", 3);
std::set<Info,InfoCmp> resetSet;
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
I see rst2 inserted twice, but it shouldn't be as per my comparator.
I see that you've come up with your own solution, after recognizing from the comments that your original did not impose a strict object ordering as required by set. Here's a different version that only requires operator< and not operator==, making it consistent with the classes and algorithms of the standard library. It also simplifies things if you're e.g. doing a case insensitive comparison.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if(lhs.getName() < rhs.getName())
return true;
if(rhs.getName() < lhs.getName())
return false;
if(lhs.getObjName() < rhs.getObjName())
return true;
if(rhs.getObjName() < lhs.getObjName())
return false;
return lhs.getDomain() < rhs.getDomain();
}
};
struct InfoCmp2 {
bool operator() (const Info &lhs, const Info &rhs) const {
return std::make_tuple(lhs.getName(), lhs.getObjName(), lhs.getDomain()) < std::make_tuple(rhs.getName(), rhs.getObjName(), rhs.getDomain());
}
};
This operator can written with make_tuple as well, and working fine.
as suggested by Cory Kramer, adding strict ordering in comparator fixed the problem.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName())
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() < rhs.getObjName()) )
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() == rhs.getObjName()) && (lhs.getDomain() < rhs.getDomain()) )){
return true;
}
return false;
}
};
I'm trying to implement a Reader class for my own hobby programming language which I'm working on. The job of the reader would be very simple, to read the source file, and remove comments.
Here's the definition of the Reader class:
// reader.hh
// Contains Reader Class specifications
#ifndef PHI_SRC_FRONTEND_READER_HH
#define PHI_SRC_FRONTEND_READER_HH
#include "errhandler.hh"
class Reader
{
public:
Reader() = default;
auto val() const -> String const & { return val_; }
void read(String const &filename);
explicit operator bool() const { return success; }
auto operator!() const -> bool { return !success; }
friend auto operator==(Reader const &lhs, Reader const &rhs) -> bool
{
return lhs.val_ == rhs.val_;
}
friend auto operator!=(Reader const &lhs, Reader const &rhs) -> bool
{
return lhs.val_ != rhs.val_;
}
friend auto operator<<(std::ostream &stream, Reader const &read) -> std::ostream &
{
return stream << read.val_;
}
private:
String val_;
bool success;
};
#endif
Previously, I used a very simple placeholder for the Reader's read function. It basically copied everything inside the file using an istreambuf_iterator
void Reader::read(String const &filename)
{
val_.clear();
success = true; // success flag, true by default
auto file = std::ifstream{filename};
if(!file)
{
log_error(Error{Error::Type::ReadError, "Could not open file"});
success = false;
}
val_.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
// Read entire file into val_
}
This worked fine, it passed the unit tests, and I manually checked the output as well, which was also fine.
But this was just a placeholder, the actual Reader needed to remove comments. Which made me implement this:
// reader.cc
// Contains Reader Class Implementation
// Work In Progress, placeholders being used for now
#include <fstream>
#include <sstream>
#include "reader.hh"
void Reader::read(String const &filename)
{
val_.clear();
success = true; // success flag, true by default
auto inStringLiteral = false;
// Variable to determine if the reader is currently reading a string literal
// (enclosed in double quotes)
// In order to not mistake '//' inside literals as comments"
auto file = std::ifstream{filename};
if(!file)
{
log_error(Error{Error::Type::ReadError, "Cannot open file: " + filename});
success = false;
return;
}
for (unsigned char c; file >> c; )
{
// ASCII characters only use 7 bits, which is up to 127
// So value of an ascii char must be lesser than 128
if (c < 128)
{
if(c == '"')
{
inStringLiteral = !inStringLiteral; // flip the value of the boolean
}
if(!inStringLiteral && c == '/')
{
// If we're not inside a string literal enclosed in quotes, and find a backslash
// Peek at the next character to check if it is a backslash
// In case two consecutive backslashes are found, treat it as a comment and
// ignore everything until the end of line
if(file >> c)
{
if(c == '/')
{
// keep reading until a newline is found
while(file >> c && c != '\n')
{
}
}
else
{
c = '/';
file.unget();
}
}
else
{
c = '/';
}
}
val_ += c;
}
else
{
log_error(Error{Error::Type::ReadError, "Unrecognized character(s) found in file: " + filename});
success = false;
return;
}
}
}
However, this weirdly causes the Unit Tests to fail.. I've compared the outputs of both versions of the read function, both have (apparently) the exact same output. Note I haven't actually checked for equality of the strings, but they do look the same. I've tried to find the reason for the error a lot but have failed....
Here's the Unit Test I'm using for the Reader (using GoogleTest):
#include <gtest/gtest.h>
#include "frontend/reader.hh"
TEST(ReaderTest, BaseTestCase)
{
auto TestReader = Reader{};
auto const ExpectedOutput = String{
R"delim(Int test = 0;
String test2 = "abcdefgh";
Float test3 = 0.9876;
)delim"};
TestReader.read("TestFiles/ReaderTest_BaseTestCase.phi");
ASSERT_FALSE(!TestReader);
ASSERT_EQ(TestReader.val(), ExpectedOutput);
// If Reader Base Test Case fails, no need to continue next tests
}
TEST(ReaderTest, Should_Fail_When_FileDoesNotExist)
{
auto TestReader = Reader{};
TestReader.read("Non_existent_test_file.txt");
EXPECT_TRUE(!TestReader);
}
As I mentioned before, it worked quite fine for the first placeholder version, but the actual read function doesn't seem to pass the tests.... The weird thing is, the sample file doesn't even have any comments, here's the sample file the Reader reads:
Int test = 0;
String test2 = "abcdefgh";
Float test3 = 0.9876;
(Yes, that's literally it. Also as I mentioned before, the language the Reader is reading is not C++, but rather a homemade language I'm working on reading, but that's probably irrelevant for this question).
Oh and in case you need to compile this, you'd need to implementation and definition of errhandler (errhandler.hh and errhandler.cc), I'll put them here as well:
Declaration (errhandler.hh):
// errhandler.hh
// Contains Phi Error Handling specifications
// Mostly complete, minor changes still might be made though
#ifndef PHI_SRC_FRONTEND_ERRHANDLER_HH
#define PHI_SRC_FRONTEND_ERRHANDLER_HH
#include <iostream>
#include "utils.hh"
class Error
{
public:
enum class Type : unsigned char
{
ReadError, LexError, ParseError, SemanticError, InterpretError
};
Error() = delete;
Error(Type type__, String const &val__) : type_(type__), val_(val__) {}
auto type() const -> Type { return type_; }
auto val() const -> String { return val_; }
friend auto operator<<(std::ostream &stream, Error const &error) -> std::ostream&
{
return stream << error.val();
}
private:
Type type_;
String val_;
};
class ErrorLog
{
public:
using iterator = Vector<Error>::iterator;
using const_iterator = Vector<Error>::const_iterator;
using reverse_iterator = Vector<Error>::reverse_iterator;
using const_reverse_iterator = Vector<Error>::const_reverse_iterator;
void push(Error const &error) { errors.push_back(error); }
void pop() { errors.pop_back(); }
auto size() const -> Size { return errors.size(); }
auto operator[](Size index) -> Error& { return errors[index]; }
auto operator[](Size index) const -> Error const& { return errors[index]; }
auto begin() -> iterator { return errors.begin(); }
auto end() -> iterator { return errors.end(); }
auto cbegin() const -> const_iterator { return errors.cbegin(); }
auto cend() const -> const_iterator { return errors.cend(); }
auto rbegin() -> reverse_iterator { return errors.rbegin(); }
auto rend() -> reverse_iterator { return errors.rend(); }
auto crbegin() -> const_reverse_iterator { return errors.crbegin(); }
auto crend() -> const_reverse_iterator { return errors.crend(); }
friend auto operator<<(std::ostream &stream, ErrorLog const &error_log) -> std::ostream&
{
for (Size i = 0; i < error_log.size(); i++)
stream << error_log[i];
return stream;
}
private:
Vector<Error> errors;
};
void log_error(Error const &error);
void show_errors(std::ostream& stream);
extern ErrorLog errlog;
// The global error log to be used by every part of the Phi system
// To be declared in main()
#endif
Definition (errhandler.cc):
// errhandler.cc
// Contains Phi Error Handling implementation
// Work In Progress, placeholders are temporarily being used
#include "errhandler.hh"
void log_error(Error const& error)
{
errlog.push(error);
}
void show_errors(std::ostream& stream)
{
stream << errlog;
}
And you also, finally, need the utils header, containing the utilities
// utils.hh
// Contains globally used utility functions, templates, using declarations, etc.
#ifndef PHI_SRC_UTILS_HH
#define PHI_SRC_UTILS_HH
#include <string>
#include <vector>
#include <limits>
#define UNUSED(x) (void)x
template <typename T>
using Vector = std::vector<T>;
using String = std::string;
using Size = std::size_t;
class bad_narrow_cast : public std::bad_cast
{
public:
bad_narrow_cast(const char* message) : what_(message)
{
}
char const *what() { return what_; }
private:
char const *what_;
};
template <typename Target, typename Base> static inline
typename std::enable_if<std::numeric_limits<Target>::is_specialized,
Target>::type narrow_cast(Base const &base)
{
if(base > static_cast<Base>(std::numeric_limits<Target>::max()) ||
base < static_cast<Base>(std::numeric_limits<Target>::min()))
{
throw(bad_narrow_cast((String() + "Invalid narrowing conversation from type " +
typeid(Target).name() + " to type " + typeid(Base).name()).c_str()));
}
return static_cast<Target>(base);
}
template <typename Target, typename Base> static inline
typename std::enable_if<!std::numeric_limits<Target>::is_specialized,
Target>::type narrow_cast(Base const &base)
{
Target target = static_cast<Target>(base);
Base narrowed_base = static_cast<Base>(target);
if (base == narrowed_base)
return target;
throw(bad_narrow_cast((String() + "Invalid narrowing conversation from type " +
typeid(Target).name() + " to type " + typeid(Base).name()).c_str()));
}
#endif
This question really halted my progress on the project. Helping me solve it would be really helpful
So, people in the comments really helped me, and suggested me to use >> for reading characters from the stream instead of eof() and get(), as eof() is unreliable.. But even that didn't solve the problem. Until I, through some googling, figured it out myself, I had to use std::noskipws, in order to make the >> operator not skip whitespaces, and then it worked. Thanks for all the help, I really appreciate it
For some reason std::map does not find my objects.
Here is my simplified object :
class LangueISO3 {
public:
enum {
SIZE_ISO3 = 3
};
static constexpr char DEF_LANG[SIZE_ISO3] = {'-','-','-'};
constexpr LangueISO3():code() {
for(size_t i(0); i < SIZE_ISO3; i++){
code[i] = DEF_LANG[i];
}
};
LangueISO3(const std::string& s) {strncpy(code, s.c_str(), 3);};
bool operator==(const LangueISO3& lg) const { return strncmp(code, lg.code, 3) == 0;};
bool operator<(const LangueISO3& lg)const { return code < lg.code;};
private:
char code[SIZE_ISO3];
};
My test is :
{
CPPUNIT_ASSERT_EQUAL(LangueISO3("eng"), LangueISO3("eng"));
std::map<LangueISO3, int> lmap;
lmap.emplace(LangueISO3("fra"), 0);
lmap.emplace(LangueISO3("deu"), 1);
lmap.emplace(LangueISO3("eng"), 2);
auto it = lmap.find(LangueISO3("deu"));
CPPUNIT_ASSERT_EQUAL(1, it->second);
}
The first test has no problem, however the second fails. lmap.find() always return lmap.end()
What did I do wrong ?
You can't compare character arrays with the < operator. When you write code < lg.code, the compiler will compare the address of the code array, and not the contents of the arrays.
Change the definition for operator< to use strncmp:
bool operator<(const LangueISO3& lg)const {
return strncmp(code, lg.code, SIZE_ISO3) < 0;
}
Also, the comparison for operator== should use the SIZE_ISO3 constant instead of hardcoding the size at 3.
I know of const, that can't be changed after creation. But I was wondering if there is a way to declare a variable that you set only once and after that, can't overwrite.
In my code, I would like to avoid the bool variable by having an nFirst that, once set to nIdx, can't be set to the new value of nIdx.
My code:
int nFirst = 0;
int nIdx = 0;
bool bFound = false;
BOOST_FOREACH(Foo* pFoo, aArray)
{
if (pFoo!= NULL)
{
pFoo->DoSmth();
if (!bFound)
{
nFirst= nIdx;
bFound = true;
}
}
nIdx++;
}
Pretty easy to roll your own.
template<typename T>
class SetOnce
{
public:
SetOnce(T init) : m_Val(init)
{}
SetOnce<T>& operator=(const T& other)
{
std::call_once(m_OnceFlag, [&]()
{
m_Val = other;
});
return *this;
}
const T& get() { return m_Val; }
private:
T m_Val;
std::once_flag m_OnceFlag;
};
Then just use the wrapper class for your variable.
SetOnce<int> nFirst(0);
nFirst= 1;
nFirst= 2;
nFirst= 3;
std::cout << nFirst.get() << std::endl;
Outputs:
1
I would like to avoid the bool variable
You can check nFirst itself, based on the fact that it won't be set a negative number. Such as:
int nFirst = -1;
int nIdx = 0;
BOOST_FOREACH(Foo* pFoo, aArray)
{
if (pFoo != NULL)
{
pFoo->DoSmth();
if (nFirst == -1)
{
nFirst = nIdx;
}
}
nIdx++;
}
Similar to cocarin's, but throws exception instead of silently ignoring assignment:
template <typename T, typename Counter = unsigned char>
class SetOnce {
public:
SetOnce(const T& initval = T(), const Counter& initcount = 1):
val(initval), counter(initcount) {}
SetOnce(const SetOnce&) = default;
SetOnce<T, Counter>& operator=(const T& newval) {
if (counter) {
--counter;
val = newval;
return *this;
}
else throw "Some error";
}
operator const T&() const { return val; } // "getter"
protected:
T val;
Counter counter;
};
Usage:
SetOnce<int> x = 42;
std::cout << x << '\n'; // => 42
x = 4;
// x = 5; // fails
std::cout << x << '\n'; // => 4
Online demo
Your question is about avoiding the bool but also implies the need for const-ness.
To avoid the bool, I'd use a boost::optional like this:
boost::optional<int> nFirst;
// ..
if (!nFirst) nFirst = nIdx;
// and now you can use *nFirst to get its value
Then, you can enforce logical (rather than literal) const-ness like this:
const boost::optional<int> nFirst;
// ..
if (!nFirst) const_cast<boost::optional<int>&>(nFirst) = nIdx;
// you can use *nFirst to get the value, any attempt to change it would cause a compile-time error
Using const_cast is not the safest practice, but in your particular case and as long as you only do it once it'd be OK. It simplifies both your code and your intentions: you do want a const, it's just that you want to defer it's initialisation for a bit.
Now, as songyuanyao suggested, you could directly use an int instead of a boost::optional, but the latter makes your intention explicit so I think it's better this way. In the end of day this is C++ while songyuanyao's solution is really a C-style one.
This is set once template. You can use this class as assurance that the value will be set and saved only once. Every next try will be canceled.
#include <iostream>
using namespace std;
template <class T>
class SetOnce;
template<class T>
std::ostream& operator<<( ostream& os, const SetOnce<T>& Obj );
template <class T>
class SetOnce
{
public:
SetOnce() {set = false; }
~SetOnce() {}
void SetValue(T newValue) { value = !set ? newValue : value; set = true; }
private:
T value;
bool set;
friend std::ostream& operator<< <>( ostream& os, const SetOnce& Obj );
public:
SetOnce<T>& operator=( const T& newValue )
{
this->SetValue(newValue);
return *this;
}
};
template<class T>
std::ostream& operator<<( ostream& os, const SetOnce<T>& Obj )
{
os << Obj.value;
return os;
}
Use case:
int main()
{
SetOnce<bool> bvar;
SetOnce<int> ivar;
SetOnce<std::string> strvar;
std::cout<<"initial values: \n"<<bvar<<" "
<<ivar<<" "<<strvar<<" \n\n";
bvar = false; //bvar.SetValue(false);
ivar = 45; //ivar.SetValue(45);
strvar = "Darth Vader"; //strvar.SetValue("Darth Vader");
std::cout<<"set values: \n"<<bvar<<" "
<<ivar<<" "<<strvar<<" \n\n";
bvar = true; //bvar.SetValue(true);
ivar = 0; //ivar.SetValue(0);
strvar = "Anakin"; //strvar.SetValue("Anakin");
std::cout<<"set again values: \n"<<bvar<<" "
<<ivar<<" "<<strvar<<" \n\n";
return 0;
}
I would like to know if there is an efficient way to remove objects from a container based on values of member fields of the objects. For example, I can do the following using stl::unique with a list of strings:
#include<iostream>
#include<list>
#include<string>
#include<algorithm>
using namespace std;
bool stringCompare(const string & l, const string & r)
{
return (l==r);
}
int main()
{
list<string> myStrings;
myStrings.push_back("1001");
myStrings.push_back("1001");
myStrings.push_back("81");
myStrings.push_back("1001");
myStrings.push_back("81");
myStrings.sort();
myStrings.erase(unique(myStrings.begin(), myStrings.end(), stringCompare), myStrings.end());
list<string>::iterator it;
for(it = myStrings.begin(); it != myStrings.end(); ++it)
{
cout << *it << endl;
}
return 0;
}
prints 1001, 81...
Is there a way I can do something similar with the following code, or do I need to perform the comparisons "manually" using operators and iterating through the containers. I couldn't think of a more elegant solution and would like to know if this is possible without writing a lot of code. Any help will be much appreciated!
class Packet
{
public:
Packet(string fTime, string rID) : filingTime(fTime), recordID(rID)
string getFilingTime() {return filingTime;}
string getRecordId() {return recordID;}
private:
string filingTime;
string recordID;
};
int main()
{
vector<Packet*> pkts;
pkts.push_back(new Packet("10:20", "1004"));
pkts.push_back(new Packet("10:20", "1004")); // not unique (duplicate of the line above)
pkts.push_back(new Packet("10:20", "251"));
pkts.push_back(new Packet("10:20", "1006"));
// remove packet from vector if time and ID are the same
return 0;
}
Thanks
Two options to be able to use std::unique:
Define an operator== method for Packet and change the vector<Packet*> to vector<Packet>.
bool Packet::operator==(const Packet& rhs) const
{
if (getFilingTime() != rhs.getFilingTime())
return false;
if (getSpid() != rhs.getSpid())
return false;
return true;
}
//etc.
int main()
{
vector<Packet> pkts;
pkts.push_back(Packet("10:20", "1004"));
pkts.push_back(Packet("10:20", "1004")); // not unique (duplicate of the line above)
pkts.push_back(Packet("10:20", "251"));
pkts.push_back(Packet("10:20", "1006"));
// remove packet from vector if time and ID are the same
pkts.erase(unique(pkts.begin(), pkts.end()), pkts.end());
return 0;
}
Keep the vector as vector<Packet*> and define a method to compare the elements.
bool comparePacketPtrs(Packet* lhs, Packet* rhs)
{
if (lhs->getFilingTime() != rhs->getFilingTime())
return false;
if (lhs->getSpid() != rhs->getSpid())
return false;
return true;
}
//etc.
int main()
{
vector<Packet*> pkts;
pkts.push_back(new Packet("10:20", "1004"));
pkts.push_back(new Packet("10:20", "1004")); // not unique (duplicate of the line above)
pkts.push_back(new Packet("10:20", "251"));
pkts.push_back(new Packet("10:20", "1006"));
// remove packet from vector if time and ID are the same
pkts.erase(unique(pkts.begin(), pkts.end(), comparePacketPtrs), pkts.end());
return 0;
}
As an alternative to unique, you could simply insert the elements into a set (or unordered_set in C++11).
Whichever way you decide to go, you'll need to define comparison operators for Packet. For unique, you'll need operator==; for set you'll need operator<. For completeness, you should define both, and their counterparts:
class Packet {
…
bool operator==(const Packet& p) const {
return fillingTime == p.fillingTime && recordID == p.recordID;
}
bool operator<(const Packet& p) const {
return fillingTime < p.fillingTime ||
(fillingTime == p.fillingTime && recordID < p.recordID);
}
bool operator!=(const Packet& p) const { return !(*this == p); }
bool operator> (const Packet& p) const { return p < *this; }
bool operator>=(const Packet& p) const { return !(*this < p); }
bool operator<=(const Packet& p) const { return !(p < *this); }
…
};
If you use C++11's unordered_set, you'll need to go one step further and define a hash function.
EDIT: I just noticed you're storing pointers to Packet. Why? Just store Packet directly.