Vectors of ofxPanel

I have used vectors of ofxPanel objects in the past and am reviving and old project. I cannot get it to work any more so I am guessing something has changed with OF, I am up to date with the current master branch of OF from git.

Here is some test code:

in ofApp.h
std::vector<ofxPanel> samplePanels;

and in ofApp.cpp

//--------------------------------------------------------------
void ofApp::setup(){
    for (int i = 0; i < 6; i++) {
        ofxPanel tempPanel;
        samplePanels.push_back(tempPanel);
        samplePanels[i].setup("Panel " + ofToString(i + 1), "settings" + ofToString(i) + ".xml");
    }
}

This does not compile and results in:

Call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<ofxBaseGui, std::__1::default_delete<ofxBaseGui> >'

How can I properly use a vector of ofxPanels?

Ok, I managed to work this out using this code:
in ofApp.h
std::vector<ofxPanel*> samplePanels;

and in ofApp.cpp

void ofApp::setup(){
    for (int i = 0; i < 6; i++) {
        ofxPanel * tempPanel = new ofxPanel();
        samplePanels.push_back(tempPanel);
        samplePanels[i].setup("Panel " + ofToString(i + 1), "settings" + ofToString(i) + ".xml");
    }
}

Hey, glad you got it to compile! Just a few thoughts on this as I had something similar recently.

I’m not sure if ofxPanel has pointers in it, but sometimes pointers and vectors have special considerations. From your compile error, I’d say that ofxPanel might have a unique_ptr in it, so it can’t be constructed and then copied into a vector with push_back. But its my understanding that this is exactly what happens when you push_back a vector; a copy of the object is added to the vector, and not the object itself.

So, there are a couple of ways to get around the copy that is made with push_back. You could make an ofxPanel object (which we’ll assume for now has a unique_ptr in it), and then std::move it into the vector, like this:

std::vector<ofxPanel> samplePanels;
ofxPanel tempPanel;
// maybe .setup() and some other stuff in tempPanel, then
panels.push_back(std::move(panel));

I’ve also seen code where the object is “constructed in place” in the vector, using emplace_back, which calls the constructor to construct the object inside the vector, rather than copying a constructed object into the vector. I’ve tried emplace_back a few times and it seemed to work well, but its been a while.

Then finally, using vectors of pointers can help in lots of situations. Its particularly helpful with oF classes like ofNode, where the class variable ofNode::parent is a raw pointer. This raw pointer can get “lost” when an ofNode is copied into a vector with push_back, or when the vector relocates itself in memory as it grows. But using a smart pointer for the object, instead of the object itself, gets around this issue. You can use either shared or unique pointers. So, in your case with ofxPanel:

std::vector<std::unique_ptr<ofxPanel>> samplePanels;
// make a panel
ofxPanel tempPanel;
// do some stuff to it if you want
// then, make a smart pointer to it on the heap, instantiated with tempPanel
std::unique_ptr<ofxPanel> ptrPanel = std::make_unique<ofxPanel>(tempPanel);
// and you can use auto if you want
auto ptrPanel = std::make_unique<ofxPanel>(tempPanel);
// and I'm not sure but you might have to std::move(tempPanel) into the new pointer
auto ptrPanel  = std::make_unique<ofxPanel>(std::move(tempPanel));
// then push_back samplePanels; use std::move for unique_ptr since they can't be copied
panels.push_back(std::move(ptrPanel));

If you did the above with a shared_ptr, then you could just push_back the vector with the shared pointer because copies are allowed.

The awesome thing about smart pointers is that they take care of their own destruction when they go out of scope, or when all of the copies have been deleted. So you don’t have to remember to delete them like raw pointers. A unique pointer will only allow 1 instance of itself ever, and you have to std::move() it around if it changes ownership. Shared pointers can have many copies of the pointer available, and it will keep track of how many objects are using the pointer at any given time.

2 Likes

hey fred,

i always get a bad feeling in the stomach when using raw pointers, because i’m afraid to introduce memory leaks. anytime you use “new” you should do “delete” as well to free the allocated memory.

if you run this just once in setup, you should be fine of course. but it could be good practice to use a smart pointer here, just to make sure it’s cleaned up.
sth like

ofApp.h

vector<shared_ptr<ofxPanel>> samplePanels;

ofApp.cpp

void ofApp::setup(){
    for (int i = 0; i < 6; i++) {
        auto tempPanel = make_shared<ofxPanel>();
        samplePanels.push_back(tempPanel);
        samplePanels[i].setup("Panel " + ofToString(i + 1), "settings" + ofToString(i) + ".xml");
    }
}

arturo wrote an article on it here:
https://openframeworks.cc/ofBook/chapters/memory.html

ha, TimChi, i see you were faster :slight_smile: i tend to almost always use shared_ptr because it’s less code in many cases.

2 Likes

@atanyimebutnow & @TimChi
Thanks for both of your suggestions. Yes ofxPanel seems to have pointers in it. I was kind of OK with the memory issue as the panels are made once in setup and used for the duration of the program. However, sometimes it is good to be smart. Both solutions work perfectly, I went with shared pointers as it looks a little neater.

I do need to read up on the differences between the two solutions proposed.

Thanks both of you.