[SOLVED] Get all Objects with same class ?



  • Hello,

    my application creates several objects of the same CustoClass in different parts of code.
    If all necessary Objects are created i need access to each of this objects.

    How can i get a list of all existing objects with the same CustomClass in the entire application ?
    (Or do i need to add to a list myself alwayswhen creating a new object )

    Maybe "QMetaObject" is a a candidate but i do not know how to.

    thnaks wally



  • why on earth would you rely on something which is not C++ (qmetaobjects etc)? just add in the class itself a static list, and in the object constuctor and destructor add/remove the objects you are creating/deleting

    @
    // custom.h
    #include <vector>
    class Custom {
    public:
    Custom();
    ~Custom();
    static std::vector<class Custom*> instances;
    ...
    }

    // custom.cpp
    #include "custom.h"
    std::vector<class Custom*> Custom::instances; // need to instantiate this because 'instances' is static
    Custom::Custom() {
    instances.push_back(this);
    ...
    }

    Custom::~Custom() {
    assert(instances.size());
    instances.pop_back();
    ...
    }
    @

    Once you have the above, you have access to 'instances' anywhere in your program (provided you #include "custom.h" in the cpp files where you use 'instances') via name qualification: Custom::instances



  • I do not know what exactly i can do with "QMetaObject",
    I'm even suprised its not C++.
    "QMetaObject":http://doc.qt.io/qt-5/qmetaobject.html#details
    "The QMetaObject class contains meta-information about Qt objects."

    Anyway, your suggestion seems to be an answer to the :
    (Or do i need to add to a list myself always when creating a new object )
    thank you very much!

    Now i need to get behind this lines:

    static std::vector<class Custom*> instances;
    ? create a static vector of (my customclass) type CustomClass ?
    and
    assert(instances.size());
    ? no idea yet ?

    Independent on my first idea QMetaObject may do this job,
    is there no way to get a list of objects of the same class in another way
    without the instances vector in class ?

    wally



  • there is no native C++ construct that allows you to access all instances of a class in a program

    of course you can create the 'instances' list outside the class

    the 'assert' statement is just a precaution to check that indeed when you delete an item from the list the list was not empty. again, it's just a precaution.

    as for static members, it's simply a member of the class which is shared by all the instances of the class, and because it is not allocated for each instance it has to be instantiated explicitly (usually the most convenient place is in the cpp file of the class). if you're not familiar with static members you should either just build a separate list, or read about static members and play around with them a little to get used with them (they are pretty powerful stuff imo, so getting used with them might well be worth it)

    PS
    i forgot to put 'public' in my class declaration, corrected that now.



  • thanks for explanation !
    Yes, playing around with this is necessary :)
    My CustomClass named : "KNhead"

    got error in knhead.cpp: here line 12
    error: undefined reference to `KNhead::instances'
    knhead.h
    @#include <QObject>
    #include <QByteArray>
    #include <assert.h>

    class KNhead
    {

    private:

    // QByteArray *intern_qba;

    public:
    KNhead();
    ~KNhead();

    static std::vector<class KNhead*> instances;
    
    QByteArray *intern_qba;
    

    ...
    @

    knhead.cpp
    @#include "knhead.h"
    #include <QDebug>

    // need to instantiate this because 'instances' is static
    std::vector<class KNhead*> instances;

    KNhead::KNhead()
    {
    intern_qba = new QByteArray("Hokuspokus");
    qDebug() << intern_qba->size();

    instances.push_back(this);       // <<<<<<<<<<<<
    

    }

    KNhead::~KNhead()
    {
    // assert(instances.size());
    // instances.pop_back();

    }@

    Maybe you see the problem immediately :)



  • oops, right, sorry, the line in the cpp file:
    @std::vector<class KNhead*> instances;
    @
    should be
    @std::vector<class KNhead*> KNhead::instances;
    @
    i.e. the instantiation should be KNhead::instances. i corrected it in the listing.

    PS
    And of course the 'custom.h' should #include <vector> and 'custom.cpp' should #include "custom.h", added these to the listing



  • great, that was also my first suspect

    As you see, i do not have experience witht Qt and C++,
    but i have to create a swarm of objects and let them communicate
    with the other using several rules. (maybe similar to neuronal net)
    Goal is to observe mofification of this net depending on communication rules.

    still a long way to go :)



  • oh yeah
    RULE OF THUMB: DO NOT USE ANYTHING SPECIFIC TO Qt whenever you don't REALLY need to, or else you'll get an app that requires Qt, and you just never know with these open source guys, today they're here, tomorrow they're all gone. Just use plain C++, no QByteArray, no signal/slots, etc, except when you really need them, and even then TRY TO CREATE YOUR OWN CLASSES AND FUNCTIONS AS WRAPPERS over what t provides so that you can easily change the framework if you'll have to


  • Moderators

    Hi,

    [quote author="wally123" date="1422710389"]I do not know what exactly i can do with "QMetaObject"[/quote]The documentation that you linked to says that QMetaObject is "not normally required for application programming, but it is useful if you write meta-applications, such as scripting engines or GUI builders." -- you usually won't need to use it for regular programs, but it provides advanced conveniences if you ever need them.

    However, QMetaObject does not give you the ability to track all instances of your custom class. You will need to implement your own tracker, like gyll said.

    [quote author="wally123" date="1422710389"]I'm even suprised its not C++.[/quote]I believe gyll meant that the meta-object system is not "plain C++". QMetaObject itself is fully implemented in C++, and can be used like any other C++ class that you might create in your applications.

    [quote author="wally123" date="1422714070"]
    @
    std::vector<class KNhead*> instances;

    KNhead::KNhead()
    {
    instances.push_back(this);
    }

    KNhead::~KNhead()
    {
    instances.pop_back();
    }
    @
    [/quote]This approach only works if you to always delete the objects in reverse order of their creation. Imagine this scenario:

    You create 3 objects ('A', 'B', and 'C'). They are added to your vector in this order ('A' is first, 'C' is last). If you then delete object 'B', its destructor calls pop_back(), which removes 'C' from the vector!

    I suggest using an unordered set instead of a vector, and removing a specific object instead of the last object.

    @
    QSet<KNhead*> instances;

    KNhead::KNhead()
    {
    instances.insert(this);
    }

    KNhead::~KNhead()
    {
    instances.remove(this);
    }
    @

    Or, if you want to stick to the STL:
    @
    std::unordered_set<KNhead*> instances;

    KNhead::KNhead()
    {
    instances.insert(this);
    }

    KNhead::~KNhead()
    {
    instances.erase(find(this));
    }
    @

    [quote author="gyll" date="1422717891"]you just never know with these open source guys, today they're here, tomorrow they're all gone.[/quote]Qt has been around for 20 years, it is backed by a commercial company, and it is spreading. I highly doubt Qt will be gone in the foreseeable future.

    [quote author="gyll" date="1422717891"]TRY TO CREATE YOUR OWN CLASSES AND FUNCTIONS AS WRAPPERS over what t provides so that you can easily change the framework if you'll have to[/quote]That sounds like a lot of extra work. Is it worth the effort? How often have you changed the framework of an established app?



  • this is really great, thanks a lot both :)


  • Lifetime Qt Champion

    Hi,

    To add to the excellent points of JKSH, you also have the "KDE free Qt foundation":https://www.kde.org/community/whatiskde/kdefreeqtfoundation.php who's purpose is to secure the availability of Qt



  • as for KDE, right ?
    but the STL also gives a cool fealing



  • JKSH: "I suggest using an unordered set instead of a vector, and removing a specific object instead of the last object."

    OUCH, that's correct, my code is TOTALLY WRONG. you have to remove the object that you are deleting, not just the last object.

    so here we go, correction:
    @
    // custom.h
    #include <unordered_set>
    class Custom {
    public:
    Custom();
    ~Custom();
    static std::unordered_set<Custom*> instances;
    }

    // custom.cpp
    #include "custom.h"
    #include <assert.h>
    std::unordered_set<Custom*> Custom::instances;

    Custom::Custom() {
    instances.insert(this);
    ...
    }

    Custom::~Custom() {
    assert(instances.count(this));
    instances.erase(this);
    ...
    }
    @

    PS
    unordered_set is C++11, so if you're using gcc you have to add to your .pro file (the Qt project file) a line like this (at least for now, until gcc parses C++11 by default):
    @
    QMAKE_CXXFLAGS += -std=c++11
    @

    you can safely make the line above the first line in your .pro file

    If you're using some other compiler (MSVC, llvm, whatever) then you might have to check the compiler's documentation for enabling C++11 support (i'm not sure about this, maybe QMAKE takes care of the translation, dunno)


  • Lifetime Qt Champion

    As for every user of the Qt library

    STL is a different beast

    @CONFIG += c++11@

    sets everything needed to use C++11



  • I'm sorry but i need more help.
    I do not know how to work on the QSet instances.

    knhead.h:
    @#include <QObject>
    #include <QSet>

    class KNhead
    {
    public:
    KNhead();
    ~KNhead();

    QSet<KNhead*> instances;
    

    };@

    knhead.cpp:
    @#include "knhead.h"

    QSet<KNhead*> instances;

    KNhead::KNhead()
    {
    instances.insert(this);
    }

    KNhead::~KNhead()
    {
    instances.remove(this);
    }@

    main.cpp:

    @#include <QCoreApplication>
    #include <QDebug>
    #include <QSet>
    #include "knhead.h"

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);
    qDebug() << "Hello\n";

    KNhead *cell_1 = new KNhead;
    KNhead *cell_2 = new KNhead;
    KNhead *cell_3 = new KNhead;
    

    // qDebug() << ??? instances.count() ??? ;

    qDebug() << cell_1->instances.count();
    qDebug() << cell_1->instances.size();
    
    qDebug() << cell_2->instances.count();
    qDebug() << cell_2->instances.size();
    
    return a.exec(&#41;;
    

    }@

    output:
    @Hello

    1
    1
    1
    1
    @

    Is there a "static" missing ?
    or do i need ust some more coffe to wake up this morning


  • Moderators

    [quote author="wally123" date="1422779095"]Is there a "static" missing ?[/quote]Yes, your set needs to be a static variable. You want all the different KNhead instances to write to the same set, after all.



  • just put a "static" in knhead.h line 10 and knhead.cpp line 3
    @
    static QSet<KNhead*> instances;
    @

    results in:

    error: undefined reference to `KNhead::instances'


  • Moderators

    You need to define static member variables in a .cpp file somewhere. Add this line to knhead.cpp (outside of any functions):

    @
    QSet<KNhead*> KNhead::instances;
    @

    Do some extra reading to understand "how to use static member variabless":http://www.learncpp.com/cpp-tutorial/811-static-member-variables/



  • Ok, now no errors and it s output looks good:
    @Hello

    3
    3
    3
    3@

    Do i need always an arbitrary instance of class KNhead to access instances
    or is there another way ?

    e.g. qDebug() << cell_1->instances.count();
    or cell_2 or cell_3



  • As i told you in my first reply (you forgot :P):
    "Once you have the above, you have access to ‘instances’ anywhere in your program (provided you #include “custom.h” in the cpp files where you use ‘instances’) via name qualification: 'Custom::instances'

    So, in any cpp file where you want to access 'instances' just make sure to #include "knhead.h" and then just use 'KNhead::instances':
    @
    #include "knhead.h"
    ...
    qDebug() << KNhead::instances.count();
    ...
    @


  • Moderators

    [quote author="gyll" date="1422783535"]
    @
    #include "knhead.h"
    ...
    qDebug() << KNhead::instances.count();
    ...
    @
    [/quote]It's a good idea to avoid globally-accessible variables where feasible. Currently, someone can add/remove items from the set, without constructing/destroying a KNhead!

    For more robust code, make the set private and implement a static member function that allows you to read the variable (but not modify it):

    @
    class KNhead
    {
    public:
    static QSet<KNhead*> KNhead::instances() {
    return m_instances;
    }

    private:
    QSet<KNhead*> m_instances; // Give it a different name from the function
    }
    @



  • w/r JKSH: using getters in the situations where you want to very clearly isolate the places where a data member changes is indeed considered the "clean code good practice" for ordinary data members, but if you have:

    1. you want to provide a static getter method
    2. you don't want to make the getter method return the actual variable because of efficiency reasons (in this particular example the getter would return a copy of the entire set each time it's called, i.e. alloc/dealloc)

    then you'd need to make the getter return a reference to the variable, and you'd need to make the getter 'const'. but you can't have const qualifiers for static methods:

    @
    static QSet<KNhead*>& instances() const; // wrong: can't specify 'const' to static methods
    @

    Alternatively, one could provide method wrappers for all the methods that are needed in your code in conjunction with the data member (it's the cleanest way, but it takes a bit of typing and code checking), e.g. you could have a static method instanceCount(), instanceList(), etc, and use one method or another as needed in the code (sure enough, instanceList() would still return a copy of the set, but only when the full set is actually needed e.g. for iterating through it)

    PS
    fwiw, i personally don't bother much with write protection (not even for ordinary data members) except when writing interfaces for objects that are to be used by "the general public"; inside the nuts and bolts my own code i just let it be



  • @class KNhead
    {
    private:
    QSet<KNhead*> m_instances;

    public:
    KNhead();
    ~KNhead();

    // static QSet<KNhead*> instances;

    static QSet<KNhead*> instances() {
        return m_instances;
    }
    
    int testProp;
    

    };@

    /knhead.h:4: error: invalid use of member 'KNhead::m_instances' in static member function
    QSet<KNhead*> m_instances;



  • you should have spotted the mistake yourself by now :P

    there's a little error there, it should be:

    private:
    static QSet<KNhead*> m_instances;

    i.e. the member still has to be static, just as it was until now, you only added a new static getter method but everything else remains the same (static methods can only access static data members of an object).
    still, i have to say it again, using getters (not to mention static getters) is in my opinion an overkill when it's not a "general public" object. If i were you i'd just use the variable 'KNhead::instances' directly where i needed it.



  • If i understnad you correctly, you recomment to use this QSet<T*>
    ( set of gathered objects created by gather method (?) )
    as public and not to use JKSH's suggestion to make it private
    and not avoid globally-accessible variables for now, because
    to much efforts to implement it (overkill).
    Is this correct ?


  • Lifetime Qt Champion

    No, you are just missing the static qualifier in from of m_instances, JKSH's technique is fine



  • I set the static already and it works now.
    My above question is meant to this :

    bq. still, i have to say it again, using getters (not to mention static getters) is in my opinion an overkill when it’s not a “general public” object. If i were you i’d just use the variable ‘KNhead::instances’ directly where i needed it.


  • Moderators

    [quote author="wally123" date="1422790846"]If i understnad you correctly, you recomment to use this QSet<T*>
    ( set of gathered objects created by gather method (?) )
    as public and not to use JKSH's suggestion to make it private
    and not avoid globally-accessible variables for now, because
    to much efforts to implement it (overkill).
    Is this correct ?
    [/quote]Yes, that is what gyll recommended.

    Both approaches work.

    • The global approach is the "fast" way, and saves you a few lines of code at the start
    • The the private approach is the "safe" way, and reduces the risk of you making mistakes in the future.

    As the program designer, you need to compare the costs and benefits of the different approaches and decide which one you want.

    I leave you with this piece of wisdom from xkcd:
    !http://imgs.xkcd.com/comics/good_code.png(Good Code)!
    (source: http://xkcd.com/844/ )



  • nice !
    now the Iterator ... :)



  • this works to find by variable name:
    @
    QSet<KNhead*>::iterator it = qFind(KNhead::instances.begin(), KNhead::instances.end(), cell_2);@

    I introduced a public property to KNhead class as
    @int testProp;@

    @KNhead *cell_2 = new KNhead;
    cell_2->testPop = 222;
    @

    Can i find/search the object in the entire QSet by this property ?
    Means, find an object in KNhead::instances with testProp == 222 ?

    Of course i can go through each object an check each on the contents of the testProp,
    i want to ask if there is a direct possibility by STL or Qt .


  • Moderators

    If you want to search by one particular property, use a QMap instead of a QSet, and use the property as the map key.

    @
    // Declaring the map
    QMap<int, KNhead*> instances;
    @

    @
    // Inserting into the map
    KNhead::KNhead(int prop)
    {
    this->testProp = prop;
    instances[prop] = this;

    // ASSUMPTION: The value of this property never changes
    

    }
    @

    @
    // Retrieving from the map
    KNhead *cell = instances[222];
    @

    If you want to search by multiple properties, use an in-memory SQLite database.



  • just a quick correction to my last reply, dunno what was in my mind when i wrote it: i said that if declaring a static getter it should be declared as 'const', and this is not possible for static methods. however, i was wrong: in order to prevent using the getter method for changing the data value, it is the return value that should be declared as 'const', not the method itself.

    So it is entirely possible to declare a clean getter method which returns a const reference:

    @
    // knhead.h
    #include <unordered_set>

    class KNhead
    {
    static std::unordered_set<KNhead*> m_instances;
    public:
    KNhead();
    ~KNhead();
    static const std::unordered_set<KNhead*>& instances();
    };

    // knhead.cpp
    #include "knhead.h"
    #include <assert.h>

    std::unordered_set<KNhead*> KNhead::m_instances;

    KNhead::KNhead() {
    m_instances.insert(this);
    }

    KNhead::~KNhead() {
    assert(m_instances.count(this));
    m_instances.erase(this);
    }

    const std::unordered_set<KNhead*>& KNhead::instances() {
    return m_instances;
    }
    @

    With the KNhead::instances() as defined above you're safe (the compiler won't let you do something like e.g. KNhead::instances().insert(x) , it will complain about you trying to change a const reference)



  • great ! thanks a lot

    and it works !



  • Hello,

    i have problem with the javastyle QMapiterator and need your help.
    Seems i do not understand the concept at all.

    the STL style iterator works nice;
    @ QMap<quint32, Indiv*>::iterator it4 = m_instances.find(this->devID);
    qDebug() << it4.value();

    // Indiv(0x8b1c378, name = "cell_0")
    // Indiv(0x8b23f08, name = "cell_1")
    // Indiv(0x8ac1138, name = "cell_2")
    // Indiv(0x8b2dbf8, name = "cell_3")
    // Indiv(0x8af8870, name = "cell_4")
    // Indiv(0x8b249f0, name = "cell_5")
    // OK@

    I played the entire afternoon with the below code,
    mostly the application crashes, sometimes i end up in a
    neverending loop. No further ideas any more now ...

    // java iterator
    @
    QMapIterator<quint32, Indiv*> it5(m_instances);

    qDebug() << it5.hasNext();  // true ok
    
    while (it5.hasNext()) {
        qDebug() << it5.value();
        it5.next();
    }@
    

    @#include <QDebug>
    #include <QObject>
    #include <QMap>
    #include <QMapIterator>@

    thx wally


  • Moderators

    [quote author="wally123" date="1422983820"]
    @
    while (it5.hasNext()) {
    qDebug() << it5.value();
    it5.next();
    }
    @
    [/quote]You need to call next() before value().



  • Yes, a fundamental issue :D

    thank you

    @
    | item 0 | item 1 | item 2 | ... | item n |

    A B C

    @

    Does the it5.next() moves the iterator from initial
    position A to B and then read item 0 ?

    I'm much too stupid for java stuff


  • Moderators

    [quote author="wally123" date="1422987332"]Yes, a fundamental issue :D

    thank you [/quote]You're welcome :)

    [quote author="wally123" date="1422987332"]
    @
    | item 0 | item 1 | item 2 | ... | item n |

    A B C

    @

    Does the it5.next() moves the iterator from initial
    position A to B and then read item 0 ?

    I'm much too stupid for java stuff[/quote]I'm not sure (I don't use Java-style iterators myself), but yes that's what the "documentation":http://doc.qt.io/qt-5/qmapiterator.html says.

    Qt's documentation is very comprehensive -- it's good to search it. If you use Google Chrome, "this tool":http://qt-project.org/forums/viewthread/36199 makes it easy to search.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.