Using ofxGui slider values outside of ofApp


#21

Yeah that is exactly what I try to explain with my poor English. :sweat_smile:
Question is, is this bug or C++ limitation?
Could we update ofxGui to be able to copy safely?


#22

no, it is not a bug.
I’d rather say it is a mixed thing between an OF design choice and a C++ limitation.

it could be done but it would be quite cumbersome I think. I am not willing to do so. @arturo what do you think?


#23

I actually start thinking this as a bug.
ofxPanel, ofxGuiGroup, ofxSlider, etc etc do not fave copy constructor.
So it can not call registerMouseEvents() during copy process.

We could delete copy constructor or add proper one.


#24

This is a limitation of the observer pattern we use for events. As you’ve said when pushing elements in the vector it’ll relocate memory some times so the pointers stored by the events are no longer valid.

If i’m not mistaken the vector will just memcpy the memory to a new location, it doesn’t call destructors or copy constructors so theevents just become invalid and they might even crash the application when called.

What we should do is mark this classes as non copiable which happens automatically when using the ofEventListener and newListener instead of ofAddListener

Also the solution right now to this is to pre-allocate or reserve memory in the vector so it never moves or to call setup in a second loop so it only registers the events after the allocation is complete

The recomended way to use gui in differen classes though is to use ofParameterGroups in those classes instead of directly panels and create the panels from those groups in one centralized place which removes this problem completely and has a much terser syntax. Something like:

class MyClass{
ofParameter<float> someParameter{"float", 0., 0., 1.};
ofParameter<int> otherParameter{"int", 0, 0, 20};
ofParameter<ofColor> colorParameter{"color", ofColor::red};
ofParameterGroup parameters{
    "settings",
    someParameter,
    otherParameter,
    colorParameter,
};

Then in a gui class or just in ofApp create as many instances of that class as needed and guis from each parameter group as:

for(int i=0; i<5; i++){
    vec.emplace_back()
    gui.add(vec.back().parameters);
}

you can have a constructor or setup method with name that sets the nameto the gui group


#25

Thanks for reply and happy new year!

I also didn’t know how std::vector relocate its memory so I checked with code below.
To be short, it calls Destructor and copy constructor.
When I uncomment and use .reserve, it does not call copy constructor, so I think I’m sure it is not called during push process. It is called for internal memory relocation process I guess. I tested on macOS, might have different behavior in other platform or compiler? This is interesting, we can actually detect whenever std::vector changes its size.

#pragma once

#include "ofMain.h"
#include "ofxGui.h"

using namespace std;

class MyClass{
    
public:
    
    MyClass(int i){
        cout << "default constructor" << endl;
        string name = "settings" + ofToString(i);
        gui.setup(name, name+".xml", i*220, 0);
        gui.add(grp);
        gui.loadFromFile(name+".xml");
    }
    
    ~MyClass(){
        cout << "destructor" << endl;
    }
    
    MyClass(const MyClass & c){
        cout << "Copy constructor" << endl;
    }
    
    MyClass(const MyClass && c){
        cout << "Move constructor" << endl;
    }
    
    MyClass & operator = (const MyClass & c){
        cout << "Copy assignment operator" << endl;
    }
    
    MyClass & operator = (const MyClass && c){
        cout << "Move assignment operator" << endl;
    }
    
    void draw(){
        gui.draw();
    }
    
    ofParameter<int> myInt{"MyInt", 0, 0, 100};
    ofParameterGroup grp{"My Class", myInt};
    ofxPanel gui;
    
};


class ofApp : public ofBaseApp{
public:
    
    
    void setup(){
        // Uncomment to see emplace_back never call destructor and copy constructor
        // vec.reserve(3);
        
        cout << "Emplace 1st element" << endl;
        vec.emplace_back(0);
        cout << endl;
        
        cout << "Emplace 2nd element" << endl;
        vec.emplace_back(1);
        cout << endl;
        
        cout << "Emplace 3rd element" << endl;
        vec.emplace_back(2);
        cout << endl;
    }
    
    
    void draw(){
        for(auto & c : vec){
            c.draw();
        }
    }
    
    vector<MyClass> vec;
    
};
// Console Oupput
Emplace 1st element
default constructor

Emplace 2nd element
default constructor
Copy constructor
destructor

Emplace 3rd element
default constructor
Copy constructor
Copy constructor
destructor
destructor

[warning] ofGLRenderer: drawing an unallocated texture
[warning] ofGLRenderer: drawing an unallocated texture
[warning] ofGLRenderer: drawing an unallocated texture
[warning] ofGLRenderer: drawing an unallocated texture
...