Find and update a custom object inside a std::set on C++ - c++

I have created 2 custom structs on my project and each one have a std::set.
struct Subject {
std::string name;
std::set<SubjectFrame> frames;
Subject(std::string subject_name);
void AddFrame(SubjectFrame &frame);
bool operator<(const Subject &rhs) const { return (name < rhs.name);}
bool operator==(const Subject &rhs) const { return (name == rhs.name);}
};
struct Dataset {
std::set<Subject> subjects;
std::map<int, std::vector<Subject> > classification_groups;
Dataset(const std::string ds_path);
void AddSubject(Subject &subject);
void GetDSFromFileSystem(const std::string dataset_path);
void GetClassificationGroups(int number_of_groups_to_create);
};
Every time I wanna add some frame to my set 'frames' I call this function:
void Dataset::AddSubject(Subject &subject) {
set<Subject>::iterator it = this->subjects.find(subject);
if (it != this->subjects.end()) {
for (Subject fr : this->subjects) {
it->AddFrame(fr);
}
} else this->subjects.insert(subject);
}
That calls this function:
void Subject::AddFrame(SubjectFrame &frame) {
set<SubjectFrame>::iterator it = this->frames.find(frame);
if (it != this->frames.end()) {
if (frame.l_eye_centroid.x != 0) {
it->r_eye_centroid = frame.r_eye_centroid;
it->l_eye_centroid = frame.l_eye_centroid;
}
if (frame.path != "") it->path = frame.path;
else return;
}
else this->frames.insert(frame);
}
So, the logic behind an add operation is: I pass an object and check if there's a object with that name already inside my std::set. If yes, I update the existent object with the informations that my parameter object has and the already registered object don't have, if no I insert the object.
I'm getting this errors when I try to compile my program:
error: no viable overloaded '='
it->r_eye_centroid = frame.r_eye_centroid;
error: no viable overloaded '='
it->l_eye_centroid = frame.l_eye_centroid;
error: no viable overloaded '='
if (frame.path != "") it->path = frame.path;
error: member function 'AddFrame' not viable: 'this' argument has type
'const Subject', but function is not marked const
it->AddFrame(fr);
Someone has any idea what is causing, and how I can solve, these problems?

For std::set, iterator-s are const, this is because they are used as key in the set which should not be modified to avoid any inconsistencies in strict weak ordering.
One way so solve it is to make the field you want to modify mutable, but make sure it is not used in your set ordering.

Related

Using QMap and getting compile error messages

I am trying to use a "QMap", in the class prototype:
QMap<QString, TargetsInfo*> m_targets;
In an access method I have:
TargetsInfo* TargetsModel::addTarget(int division, int camera, int map) const {
TargetsInfo* target = getTarget(division, camera, map);
if (target == nullptr) {
const QString key = makeKey(division, camera, map);
target = new TargetsInfo();
if ( target != nullptr ) {
m_targets.insert(key, target);
}
}
return target;
}
TargetsInfo* TargetsModel::getTarget(int division, int camera, int map) const {
const QString key = makeKey(division, camera, map);
QMap<QString, TargetsInfo*>::iterator itr = m_targets.find(key);
return (its == m_targets.end()) ? nullptr : itr.value();
}
When I compile I get the following errors which I am struggling to see what is wrong:
C2663: 'QMap<QString, TargetInfo *>::insert': 2 overloads have no legal conversion for 'this' pointer
C2440: 'initialising': cannot convert from 'QMap<QString, TargetInfo *>::const_iterator' to 'QMap::<QString, TargetInfo *>::iterator'
No constructor could take the source type, or constructor overload resolution was ambiguous
For the second error change
QMap<QString, TargetsInfo*>::iterator itr = ...
to
QMap<QString, TargetsInfo*>::const_iterator itr = ...
Your getTarget method is const so m_targets.find returns a const iterator (otherwise you could change m_targets inside your supposedly const method).
For the first error, remove const from your addTarget method. Obviously a method which adds items to m_targets should not be const.

I try to use MAP container to map resources, why is this not woring?

This is giving me an operator error when I compile it. No other error show up.
#include <map>
struct RESOURCE {
char Name[MAX_PATH] = { NULL };
int Level = 0;
};
struct RESOURCEFILE {
char FileName[MAX_PATH] = { NULL };
DWORD ATTRIBUTE = 0;
};
map <RESOURCE, RESOURCEFILE> ResourcesMap;
void PolulateResources(RESOURCE Resource, RESOURCEFILE File){
ResourcesMap[Resource] = File;
};
The reason this doesn't compile is because you are trying to use a map with key type RESOURCE. By default the compare function for std::map uses less-than-operator(<) for the key type to order the map. You haven't defined one here.
You can fix the issue by defining a operator< for RESOURCE:
struct RESOURCE {
char Name[MAX_PATH] = { NULL };
int Level = 0;
bool operator<(const RESOURCE& other) const
{
//write your compare function here...
//return (strcmp(Name, other.Name) < 0);
}
};
(It might be easier to use std::string for Name since it already has a operator<).
Alternatively you could define a functor or a lambda for the compare function.

How to get function pointer arguments names using clang LibTooling?

Say I analyze a code like this:
struct Foo
{
void(*setParam)(const char* name, int value);
};
I use clang LibTooling and get FieldDecl on a setParam.
I figured I can get argument types like so:
auto ft = fieldDecl->getFunctionType()->getAs<FunctionProtoType>();
for (size_t i = 0; i < fpt->getNumParams(); i++)
{
QualType paramType = fpt->getParamType(i);
....
}
But how do I get argument names? ("name" and "value" in that case) Is that even possible or I need to manually look into source (with SourceManager)?
I don't think it's possible to get the parameter names directly from the type, since they're not part of the type information.
But your task can be accomplished by one more visiting to the function pointer declaration:
class ParmVisitor
: public RecursiveASTVisitor<ParmVisitor>
{
public:
bool VisitParmVarDecl(ParmVarDecl *d) {
if (d->getFunctionScopeDepth() != 0) return true;
names.push_back(d->getName().str());
return true;
}
std::vector<std::string> names;
};
Then the calling site is:
bool VisitFieldDecl(Decl *d) {
if (!d->getFunctionType()) {
// not a function pointer
return true;
}
ParmVisitor pv;
pv.TraverseDecl(d);
auto names = std::move(pv.names);
// now you get the parameter names...
return true;
}
Pay attention to the getFunctionScopeDepth() part, it's necessary because a function parameter might be a function pointer itself, something like:
void(*setParam)(const char* name, int value, void(*evil)(int evil_name, int evil_value));
getFunctionScopeDepth() being 0 ensures that this parameter is not in a nested context.

Strange issue when iterating a STL set<CStudent> in C++

I have class CStudent and class CStudentGroup which has one member set<CStudent>. I populate the set of an object from the class CStudentGroup. I want to iterate this set and print via the getter of the CStudent class the points of all the students in the set. I do this by assigning the set to a new one. Then I iterate the set with an iterator it. However the compiler gives an error *the object has type qualifiers that are not compatible with the member function CStudent::getP; object type is const CStudent* I would like to ask how can I do this? Thank you in advance.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class CStudent {
string m_strFN;
int m_iPoints;
public:
void setP(int p) {
m_iPoints = p;
}
void setFN(string f) {
m_strFN = f;
}
int getP() {
return m_iPoints;
}
string getFN() {
return m_strFN;
}
CStudent() {
m_strFN = "123456789";
m_iPoints = 70;
}
CStudent(const CStudent& stud) {
m_strFN = stud.m_strFN;
m_iPoints = stud.m_iPoints;
};
CStudent(int p) {
m_iPoints = p;
}
};
class CStudentGroup {
set<CStudent> m_setStudents;
public:
CStudentGroup(const CStudentGroup& grp) {
m_setStudents = grp.m_setStudents;
};
CStudentGroup(set<CStudent> st) {
m_setStudents = st;
}
CStudentGroup() {
CStudent s1(50), s2, s3(s2);
m_setStudents.insert(s1);
m_setStudents.insert(s2);
m_setStudents.insert(s3);
}
set<CStudent> gets() {
return m_setStudents;
}
};
int main()
{
CStudentGroup group;
set<CStudent> stt = group.gets();
for (set<CStudent>::iterator it = stt.begin(); it != stt.end(); it++) {
cout << it->getP() << endl;
}
}
std::set stores keys as constant value, as a change of a key can be a cause of change to its position in red-black tree (typical std::set implementation).
In other words, your CStudent object are considered const or unchangeable.
It's possible to problem here using std::set::const_iterator as a type of iterator inside the loop in combination with std::set::cbegin() and std::set::cend() calls.
Another possible solution is to use foreach-loop:
for (CStudent const& student : stt)
std::cout << student.getP() << '\n';
Moreover, you would need to change CStudent::getP() declaration to be a constant method.
Objects inside a std::set are always const. That is to protect them, in case you decide you change any key field, the sorting order changes and the set invariant is broken.
So basically the set<CStudent>::iterator is a const_iterator and you get a const CStudent& reference. Since your CStudent::getP is not a const member function, you cannot use it.
Solution, make it const:
int getP() const {
return m_iPoints;
}
Naturally, you want to mark as const any function that does not change the contents of your object, not only the ones std::set requires you to do so. This is sometimes called const-correctness and is always a good practice.

Find an element in std::set by a custom comparator

I created a class Route which I wanted to store in an std::set. A Route is indexed by an Id, so what I want is to be able to have an expression like
class RouteTemplate
{
Route *RouteTemplate::getRoute(const char *pId);
Route::ptr_set mRoutes;
};
Route *RouteTemplate::getRoute(const char *pId)
{
Route::ptr_set::const_iterator pos = mRoutes.find(pId);
if(pos == mRoutes.end())
return NULL;
return *pos;
}
However I get a compiler error.
conversion from 'const char *' to 'Route *const ' not possible
As far as I know I have to implement the comparator, which I did.
class Route
{
public:
static const size_t _id_len = 11;
class comparator
{
public:
bool operator() (const Route &oLeft, const Route &oRight) const
{
return oLeft < oRight;
}
};
class ptr_comparator
{
public:
bool operator() (const Route *oLeft, const Route *oRight) const
{
return (*oLeft) < (*oRight);
}
};
typedef std::set<Route, Route::comparator> set;
typedef std::set<Route *, Route::ptr_comparator> ptr_set;
public:
Route(void);
Route(const char *oId);
virtual ~Route(void) {};
inline bool operator<(const Route &oOther) const
{
return strncmp(mId, oOther.mId, _id_len) < 0;
}
inline bool operator<(const char *oId) const
{
if(!oId)
return false;
return strncmp(mId, oId, _id_len) < 0;
}
inline const char *getId(void) const { return mId; }
inline void setId(const char *oId)
{
if(oId == NULL)
mId[0] = 0;
else
{
strncpy(mId, oId, sizeof(mId));
mId[_id_len] = 0;
}
}
private:
char mId[_id_len+1];
// Additional members
};
I assume you want to leverage the templated overload of std::set::find that was added in C++14. Before that, you could only find() a key of the Key type that is used for the std::set. So, the first thing to do is using a C++14 compiler.
Second, that additional overload can only function if the resulting comparison has the same semantics as would have constructing a (temporary) key and comparing it with the std::sets comparator. If I'm not missing anything, your comparators would qualify for this. However, to avoid accidental mistakes, you have to explicitly confirm that by giving the Compare type a type member is_transparent.
If you can live with a temporary being created, you could explicitly ask for it. This should work.
Route *RouteTemplate::getRoute(const char *pId)
{
Route temporary_key {pId};
Route::ptr_set::const_iterator pos = mRoutes.find(&temporary_key);
if(pos == mRoutes.end())
return NULL;
return *pos;
}
You could also overload operator&, to allow invoking it on temporary object. This would simplify usage o find method as you could create Route object on the fly and then apply operator& at this temporary.
class Route
{
public:
...
Route* operator&() { return this; }
...
}
Then it would be valid to write getRoute() method like:
Route *RouteTemplate::getRoute(const char *pId)
{
Route::ptr_set::const_iterator pos = mRoutes.find(&Route(pId));
if (pos == mRoutes.end())
return NULL;
return *pos;
}