ofParameterGroup and custom inherited ofParameter class

I’m a bit confused. I’m inheriting the ofParameter class and trying to add that to an ofParameterGroup. The addition works fine however when I want to cast back to my own class when iterating the group it is not working. It works fine when I just use a vector<NodeParameter*>

i.e:

template<typename ParameterType>
class NodeParameter: public ofParameter<ParameterType>
{
public:
    NodeParameter() { test = "foo";};
    virtual ~NodeParameter() {};
    std::string test;
};

NodeParameter<int> count;
count.set("counter", 0);
ofParameterGroup group;
group.add(count);
for ( auto param : group )
{
            auto pad1 = std::dynamic_pointer_cast<ofParameter<int>>(param);
            ofLogVerbose() << pad1;
            auto pad2 = std::dynamic_pointer_cast<NodeParameter<int>>(param);
            ofLogVerbose() << pad2;          
}

the pad2 is always 0. Any ideas why?

I might have found the cause. Not sure yet but I think it is caused by the add() method of ofParameterGroup creating a shared_ptr by newReference(). This will create a shared_ptr instance of the type of my parent class (ofParameter). Therefore at runtime it doesn’t know about the child classes hence always return a null when dynamic_casting to a child class.

A solution might be to override the newReference() method. Testing this now

i wouldn’t recommend extending ofParameter that way but instead including it in your own class, what are you trying to do?

I’m currently researching whether I can use ofParameters as a base for a node editing system build using OF. So the ofParameters are used as the parameters of nodes which can communicate with other parameter’s nodes. Basically the parameters are linked to each other using the events.

I needed some extra meta data for the parameters therefore I created a class derived from ofParameter. I also inherited another class from the node UI which enabled me to use the parameters agnostic from the types.

My class looks like this:

template<typename ParameterType>
class NodeParameter: public ofParameter<ParameterType>, public ImGui::NodePadType
{
public:
    NodeParameter() { access = "rw";};
    virtual ~NodeParameter() {};
    virtual void onReceive(ParameterType& value) { ofLogVerbose() << value; };

    std::shared_ptr<ofAbstractParameter> newReference() const{
        return std::make_shared<NodeParameter<ParameterType>>(*this);
    }
};

It works, sort of experimental, but I’m not yet happy as I’m expecting a lot of casts for all the parameter types.

Hi sphaero!
I was in exactly the same position as yours.

What I ended up doing was creating a map that stores a struct with metadata for each parameter, with the parameter name as a reference in the map.
std::map<string, parameterInfo> parametersInfo. (parameterInfo is my struct that stores more data for the parameter)
you can see implemented here: https://github.com/PlaymodesStudio/ofxOceanode/blob/master/src/Nodes/ofxOceanodeNodeModel.h
Depending on your class structure this for sure is an issue, but with my class structure seems to be not a bad thing.

Probably there is a deeper discussion about something I have been thinking about, having some sort of ofParameterExtension instance inside ofParameter, and you can create a class that inherits from ofParameterExtension and create your own extras for parameters. This would keep ofParameter as simple as it is intended to be, but helpful for creating more complex ofParameter usage.

I also spent a lot of time struggling with connections trying not to do casting for each type. It’s a quite complex project but is a node based frameworks that uses ofParameters and it’s event system under the hood. (seems similar of what you are trying to achieve)


Here you have it if you want to dig in the code and see how I solved some of the problems you will encounter.

Best
Eduard.

Thanks for that @EduardFrigola . Nice to see some other approach. What’s your experience regarding performance?

Actually the multi-inheritance approach I’m using seems to work fine up until now. I’m wondering what the negatives are of inhering the ofParameter class @arturo. The ofParameter classes are a bit scarce in documentation so I’m mostly taking it from tryout approach.

Rg,

Arnaud

in general inheritance is problematic and multiple inheritance is terrible :slight_smile:

if you can instead have a parameter and the gui objects as members of your class and expose the methods you need it’s usually a better solution.

In this case you can extend ofAbstractParameter as well which will allow you to add your custom class to a parameter group but in practice has no implementation and more important holds no data which is mostly where inheritance problems usually come from.

Your class would look something like:

template<typename ParameterType>
class NodeParameter: public ofAbstractParameter
{
public:
    NodeParameter() { test = "foo";};
    virtual ~NodeParameter() {};
    std::string test;

   // implement the mandatory methods from ofAbstractParameter here
  // also expose any extra methods you need from the ofParameter and node members
private:
    ofParameter<ParameterType> parameter;
    ImGui::NodePadType node;
};

This approach usually avoids a lot of headaches in the long term but by all means if your solution solves your problem, mostly if this is not for a public facing api, like an addon, go with whatever is simpler for you to implement and use

1 Like

Indeed it is but its one of the main features of c++ :confounded:. I’m still designing and getting used to ofParameters. One of the features I’m designing for is to have a very simple Node class which can be used to render a UI however it’s optional to have some headless support. Least overhead possible.

I’d like to use all the parameter types of OF so inheriting from ofAbstract parameter doesn’t help me or am I missing something?

I wouldn’t say so, take a look at the c++ standard library and see if you can find many places where it uses public inheritance in this way .

Extending ofAbstractParameter allows you to add a new type of parameter into a parameter group, guis… The inner parameter is what defines the type you want to use and since it’s templated it allows you to use any type the same way it would by using inheritance

The implementation is more verbose than using inheritance but allows you to be more selective about which methods you want to publish from the parameter and the node rather than publishing absolutely everything

Thanks for the pointer. I’m going to look into this.

Btw, the newListener() method ofParameter accepts lambdas, but addListener doesn’t right?

One is supposed to maintain a table of listeners returned from newListener(), right? They are not tracked somewhere else?

yes if you want to use lambdas you need to use newListener cause there’s no way to use removeListener with lambdas. and yes you need to keep the listeners somewhere, there’s a specific class to do so, ofEventListeners which just removes all listeners when destroyed. if you want finer grained control over listener removal then you’ll need to keep an index of listeners per parameter or just store them as individual variables

I tried your approach. Works nicely as well, cleaner code :slight_smile:

template<typename ParamType>
class NodeParameter : public ofAbstractParameter {
public:
    ofParameter<ParamType> param;
    std::map<std::string, ofEventListener*> listeners;
    std::string getName() const { return param.getName();}
    void setName(const std::string& name ) { param.setName(name); }
    std::string toString() const { return param.toString(); }
    void fromString(const std::string& str) { return param.fromString(str);}
    std::string valueType() const { return param.valueType(); };
    void setParent(ofParameterGroup &_parent) { param.setParent(_parent);}
    bool isSerializable() const { return param.isSerializable();}
    bool isReadOnly() const { return param.isReadOnly();}
    std::shared_ptr<ofAbstractParameter> newReference() const { return param.newReference();} //TODO
    const ofParameterGroup getFirstParent() const { return param.getFirstParent();}
    void setSerializable(bool serializable) { param.setSerializable(serializable);}
    const void* getInternalObject() const { return param.getInternalObject();}

    ofEventListener* subscribe(ofAbstractParameter& tgtparam) {
        ofEventListener* ev = new ofEventListener();
        NodeParameter<int>& paramInt = dynamic_cast<NodeParameter<int>&>(tgtparam);
        *ev = param.newListener([&paramInt](const ParamType &v){ paramInt.param.set(v); });
        listeners.insert(std::pair<std::string, ofEventListener*>(tgtparam.getName(), ev));
        return ev;
    }

    bool unsubscribe(ofAbstractParameter& tgtparam) {
        return unsubscribe(tgtparam.getName());
    }
    bool unsubscribe(std::string tgtname) {
        std::map<std::string, ofEventListener*>::iterator it = listeners.find(tgtname);
        if ( it != listeners.end() )
        {
            delete it->second;
            listeners.erase(it);
            return true;
        }
        return false;
    }
}; 

I also tested the performance of ofParameter:

    paramOut.param.set("out", 0);
    paramCount.param.set("count", 0);
    paramLog.param.set("log", 0);
    group1.add(paramOut);
    group1.add(paramCount);
    group2.add(paramLog);

    uint64_t start = ofGetElapsedTimeMicros();
    int count = 0;
    for (int i=0;i<1000000;i++){
        count++;
    }
    ofLogVerbose() << "int count time: " << (ofGetElapsedTimeMicros()-start)/1000000.;

    start = ofGetElapsedTimeMicros();
    count = 0;
    for (int i=0;i<1000000;i++){
        paramCount.param++;
    }
    ofLogVerbose() << "param count time: " << (ofGetElapsedTimeMicros()-start)/1000000.;

    paramOut.subscribe(paramCount);
    paramCount.subscribe(paramLog);

    start = ofGetElapsedTimeMicros();
    count = 0;
    for (int i=0;i<1000000;i++){
        paramCount.param++;
    }
    ofLogVerbose() << "subscribed param count time: " << (ofGetElapsedTimeMicros()-start)/1000000.;

    start = ofGetElapsedTimeMicros();
    count = 0;
    int p = paramCount.param.get();
    for (int i=0;i<1000000;i++){
        p++;
    }
    paramCount.param.set(p);
    ofLogVerbose() << "subscribed optimized param count time: " << (ofGetElapsedTimeMicros()-start)/1000000.;

    ofExit();

Output:

[verbose] int count time: 0.008483
[verbose] param count time: 2.1906
[verbose] subscribed param count time: 6.2919
[verbose] subscribed optimized param count time: 0.008477

I guess you don’t want to use parameters for calculations :thinking:
But this was to be expected.

I’m not so happy with the required type casting but I guess it needs a completly different design to be type agnostic.

1 Like

@arturo, do you know if ofFastEvent can be used for ofParameter?

yes ofParameter is not meant to be very fast, whenever you set a value it triggers an event which by default is relatively slow since it’s thread safe, it makes sure that there are not recursive notifications…

There’s no way to use a fast event right now but it would be pretty straight forward to add a second parameter to ofParameter that defaults to ofEvent but could be specified as an ofFastEvent.

If you want to create a github issue we can look at it to include it in the next major release