Use setup() vs. constructor

Is there a particular reason (other than “this is how it was done in Processing”) for the oF convention of doing all of the setup in the setup function, instead of in a class constructor?

@kflak1 - there are a few reasons not to use a constructor.

1 - If you declare a constructor you are meant to also declare a destructor and possibly a copy constructor which adds complexity to a class.
2 - It doesn’t allow you to control when you initialize / load data unless your use new and a pointer.
3 - For a while now you can set default values for types in the class definition itself. eg:

class myClass{
public:

float mAlpha = 255.0; 
};

In the past people used constructors as a way to set sane values for variables, but this isn’t needed now with the above.

4 - If you constructor takes arguments it can’t be constructed easily without passing those arguments and that also restricts how you can use it.

These are very much off the top of my head. There are some good use cases for constructors but I would argue that they are less frequent for OF projects and often make code more complex and error prone. ( especially with std::vectors of objects ).

Hope that is helpful.
I am sure I missed a bunch of reasons for/against. :slight_smile:

Theo

2 Likes

Great, thanks for the clarification, this is exactly the kind of information I was looking for :slight_smile:

1 Like

Also to add to theo’s comments, a related topic is the rule of 3 (or 5) in c++; I’ve always liked this reference:
https://en.cppreference.com/w/cpp/language/rule_of_three

1 Like

if i may also add: in the context of openFrameworks, where the “user” is often beginning learning to work with code, it helps enforce an extra separation of the levels of initialization, where the “low level” init is done in the constructor (which happens exactly once in the lifetime of a typical .h-declared non-pointer object), and the user-level “configuration” is done in the setup() – specifically for classes that may have complex behind-the-scene initializations or ressources that you don’t want to disturb during the update/loop render cycle.

it’s also good to know that once you’re executing the app’s setup() calling your objects’ setup()s all the ofApp machinery is ready to run; it keeps the whole pattern tidy.

that being said, as a comfortable user i’m fond of declaring:

ofParameter<float> zoom{"zoom",1,.1,10};

as it cuts a semi–repetitive line from the app’s setup(). i would not mind being able to declare for instance ofxOscSender sender{"127.0.0.1",2000}… it would be nice if “light” classes could have setupish constructors (like ofParameter does). it would also facilitate the use of pointers where std::make combines allocation and initialization.

3 Likes

The ofParameter class is interesting this way. In a recent project I had a std::vector<ofParameter<glm::vec3>> with my own default values in ofApp.h. So being able to call the its constructor with values was fantastic. An example with floats:

// in ofApp.h
    std::vector<ofParameter<float>> params {
        ofParameter<float>("first", 0.5f, 0.f, 1.f),
        ofParameter<float>("second", 5.f, 1.f, 10.f),
        ofParameter<float>("third", 50.f, 10.f, 100.f),
    };

So, a custom class can definitely benefit from having special constructors. I think a default constructor can still be defined with MyClass() = default;. But I’m not really sure if/when you need to declare one, even after quickly reading:
https://en.cppreference.com/w/cpp/language/default_constructor

the constructed-in-place vector is cool but FYI you can do this with glm:

ofParameter<glm::vec3> params{"params", {0.5,5,50},{0,1,10},{1,10,100}};

I won’t pretend to grasp all the intricacies of the construction/destruction/rule-of-3/5 subject, but as I work with it:

  • you can do pretty much what you want in a constructor, as long as you’re using RAII things (or “prepare” member values, do logging, etc) and use trivial (default-constructible) data members.

  • things get more complicated when copying your object is non-trivial (or when copying it is illogical), uses pointers (do you copy the pointer or the ressource being pointed?), or when you need to de-allocate/stop non-RAII things; then you need 3/5 definitions.

I find this slightly more readable than cppreference: IBM Documentation

1 Like

Wow, the IBM docs are indeed much more readable! Thanks for the pointer.

@kflak1 yes! keep in mind they are for their z/os product, so for “absolute documentation” cppreference has authority, but in terms of explaining concepts the IBM style is often clearer.

Another good reason to use ofApp::setup() is that you are (almost!) guaranteed that the OpenGL context has been initialized by then. If your class constructor creates or uses OpenGL resources (textures, shaders, VBO’s, etc.), and it is called prior to the creation of an OpenGL context, your app will likely crash. You might run into this if you declare your custom type somewhere in main.cpp, or even in a header, and these bugs can be confusing to track down!

3 Likes