How to use a template object and keep it for next block?

Hi,
absolute noob to C++ and VST3 here.

I want to create a “library” (separate .h file) which contains only one template class for the types of double and float.
For simplicitys sake I’ll provide pseudo code.

audioOP.h
template
class audioOP
{
public:
audioOP()
{
some code
}
~audioOP()
{
some code
}
void setSize(T size_to_set)
{
sizevar = size_to_set;
}
private:
T sizevar;
};

now in processor.cpp, I include my audioOP.h
then, how do I go about declaring a pointer to that object so I can use the object in any method / function of the processor.cpp?

my current approach is to do:
1.
in processor.h protected:
audioOP* audioOPF;
audioOP* audioOPD;
2.
in processor.cpp setupProcessing after testing whether channel layout has been accepted successfully:
audioOPF = new audioOP();
audioOPD = new audioOP();

And then in the processAudio lambda, each time I want to use that object, I have to do a switch case to select the right one.

I’ve been googling the past days but I couldn’t find a good way to do this.
Vitrual member functions (base+derived) appear to have a performance penalty so we can’t use it in the audio path.
Also it appears the type has to be known for the virtual declarations in the base already so that doesn’t improve anything.

Basically what I want is to ONCE determine which variant (double, float) is needed to process this block and then store a pointer to the right object pointer (audioOPF / audioOPD).
And since I want to create my audioOP objects only once to hold their member values and I won’t know ahead of time whether processAudio will be called on 64bit or 32bit audio that pointer needs to be “typeless” / “generic”.
But how do I do that?

Or is there a more elegant way of doing this?

Hi, there is a simple solution using virtual functions. The performance penalty for virtual functions is low (if present at all, they are sometimes optimised away) and you can write it in a way that it is e.g. only called once per processing block. This is okay! The AudioEffect class which you must be using also uses virtual functions and everyone uses that.

You can write your base like this:

struct Base {
virtual process(Vst::ProcessData& data) = 0;
};

and the implementation like this:

template<class T>
struct Derived : Base {
void process(Vst::ProcessData& data) override {
}
};

In the processor class that derives from Vst::AudioEffect you declare a std::unique_ptr<Base> myPtr which you can initialize based on the sample type when it is known:

if(/*float*/)
  myPtr = std::make_unique<Derived<float>>;
else
  myPtr = std::make_unique<Derived<double>>;

No issues with needing the type in the base class (although sometimes you need to get creative, here Vst::ProcessData manages that).

For parameters like size you could maybe use double always! At least in the setter

Cheers

Hi Mc-Zen,
thank you very much for taking the time to explain such a mundane question!
It’s a great help and pointed me in the right direction, though I wanted my base class to be non-VST-specific as there are some libraries that rely on juce and I wanted to “free” them, so my intention was to use straight up float and double for the (array) types.
A couple of days back I had the idea to ask chatGPT but postponed, I just did and in 3 minutes it gave me this:

#include <iostream>

class Base {
public:
    virtual void foo() = 0;
};

template <typename T>
class Derived : public Base {
public:
    virtual void foo() {
        std::cout << "foo<" << typeid(T).name() << "> was called" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived<int>();
    Base** ptrPtr = &ptr;
    (**ptrPtr).foo();
    delete ptr;
    return 0;
}

Hail the AI overlords!
also it came up with

#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <memory>

class Base {
public:
    virtual void foo() = 0;
};

template <typename T>
class Derived : public Base {
public:
    virtual void foo() {
        std::cout << "foo<" << typeid(T).name() << "> was called" << std::endl;
    }
};

int main() {
    std::type_index typeIndex = typeid(int);
    std::unique_ptr<Base> ptr;
    if (typeIndex == typeid(int))
        ptr.reset(new Derived<int>());
    ptr->foo();
    return 0;
}

Basically that is what I was looking for!
Or do you know any other way of doing this?
If I process an entire buffer it’s fine to call a virtual but I imagine if I did that in the processing loop on a sample-by-sample basis my CPU would get very angry :smiley:

Mmh

Well aren’t these just the same solution but without the ProcessData parameter? I understand the idea with making your class independant, a nice workaround would be to have an intermediate class like that

You can write your base like this:

struct Base {
virtual process(Vst::ProcessData& data) = 0;
};

and the implementation like this:

template<class T>
struct Derived : Base {
void process(Vst::ProcessData& data) override {
}
};

And then make your class a member of that. Here, the float type is already known and no further virtual functions are needed. I would recommend to call the intermediate class’s process function blockwise with ProcessData and forum within that you can easily call your other class’s function sample-wise.

template<class T>
struct Derived : Base {
void process(Vst::ProcessData& data) override {
    // get sample buffers
    // ...
    for (int i = 0; i < data. ....) {
        out[i] = filter.process(in[i]);
    }
}
MyFilter<T> filter;
};

Let me know if this helps!

PS: please don’t use new if you can help it :slight_smile: (std::make_unique() is the best choice for creating a unique pointer in most cases).