2

I'm writing a VST in C++ currently, and I ran into an issue. I've been surfing the web and StackOverflow for a while now and I'm stuck. Two files are giving me the issue, SynthesizerTwo.h and SynthesizerTwo.cpp.

The error I get is:

'kNumPrograms': undeclared identifier [in SynthesizerTwo.h]

I have a const int called kNumPrograms that is declared like this in SynthesizerTwo.cpp:

#include "SynthesizerTwo.h"

#pragma warning( suppress : 4101 4129 )
#include "IPlug_include_in_plug_src.h"

#include "IControl.h"
#include "resource.h"

const int kNumPrograms = 1; //Original Declaration

enum EParams
{
    kNumParams
};

Then, in SynthesizerTwo.h, it is used in the class SynthesizerTwo like this:

#ifndef __SYNTHESIZERTWO__
#define __SYNTHESIZERTWO__

#include "Oscillator.h"
#include "MIDIReceiver.h"

#pragma warning( suppress : 4101 4129 )
#include "IPlug_include_in_plug_hdr.h"

class SynthesizerTwo : public IPlug
{

public:

  SynthesizerTwo(IPlugInstanceInfo instanceInfo);
  ~SynthesizerTwo();
  //const int kNumPrograms = 5;
  void Reset();
  void OnParamChange(int paramIdx);
  void ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames);

  // to receive MIDI messages:
  void ProcessMidiMsg(IMidiMsg* pMsg);

private:
  double mFrequency;
  void SynthesizerTwo::CreatePresets() {
      
      MakeDefaultPreset((char*)"-", kNumPrograms); //This is what gives me the error
  }
  Oscillator mOscillator;

  MIDIReceiver mMIDIReceiver;
};

#endif

I've tried putting the declaration into the .h file, but then I get an LNK2019 linker error. I've also tried using an extern before the declaration in both the .cpp. and the .h.

I've tried putting the declaration outside the class in the .h, in the public section, and in the private section. I've also tried moving the declaration in the .cpp above the .h #include but I still get errors.

Linker error:

Severity    Code    Description Project File    Line    Suppression State
Error   LNK2019 unresolved external symbol "public: void __thiscall MIDIReceiver::advance(void)" (?advance@MIDIReceiver@@QAEXXZ) referenced in function "public: virtual void __thiscall SynthesizerTwo::ProcessDoubleReplacing(double * *,double * *,int)" (?ProcessDoubleReplacing@SynthesizerTwo@@UAEXPAPAN0H@Z) SynthesizerTwo-vst2 C:\wdl-ol\IPlugExamples\SynthesizerTwo\SynthesizerTwo.obj   1   

Any help would be much appreciated.

Josi Whitlock
  • 185
  • 4
  • 15
  • `void SynthesizerTwo::CreatePresets()` should be `void CreatePresets()`. Move the implementation of that function into the `.cpp` file (after the declaration of `kNumPrograms`). – Ted Lyngmo Jul 23 '20 at 15:42
  • Unrelated: Why are you casting `(char*)"-"` instead of just calling `MakeDefaultPreset("-", kNumPrograms);`? – Ted Lyngmo Jul 23 '20 at 15:43
  • 1
    Thanks, I'll try that. – Josi Whitlock Jul 23 '20 at 15:43
  • Hmmm. I'm not sure. I'll have to take a look. – Josi Whitlock Jul 23 '20 at 15:44
  • I'm casting because the MakeDefaultPreset() calls for a `char*` – Josi Whitlock Jul 23 '20 at 15:52
  • In that case, it does so to be able to change the content. If it tries to change the content of your `"-"` you have undefined behavior. Your program might crash or do something really strange. Don't cast. Give it an array that it's allowed to change. Do you have a link to the documentation of `MakeDefaultPreset()`? – Ted Lyngmo Jul 23 '20 at 15:53
  • Alright. I'm using a framework, and so I'm hesitant to change things, but I'll try it. – Josi Whitlock Jul 23 '20 at 15:59
  • If the framework expects a parameter like `char*` instead of `const char*` it means that it might change the content of what the `const char*` is pointing at. Doing so has undefined behavior. Do you have a link to the documentation for `MakeDefaultPreset`? I want to read about what it does with the first parameter. – Ted Lyngmo Jul 23 '20 at 16:01
  • I don't, but I can provide the GitHub page to the original repo: https://github.com/olilarkin/wdl-ol – Josi Whitlock Jul 23 '20 at 16:03
  • That'll do. I downloaded the framework. I wouldn't use that. `MakeDefaultPreset` actually does _not_ change the `name` argument. It just copies it. If you supply a `nullptr` instead, it'll use the name `Empty`. Go for that instead of casting. The method _should_ be `const char*` because, as I said, it does not change what the pointer is pointing at. – Ted Lyngmo Jul 23 '20 at 16:10
  • 1
    Unrelated: Since the framework you linked to has been archived and they link to the new framework, [iPlug2](https://github.com/iPlug2/iPlug2), perhaps you'd be better off starting with that instead? – Ted Lyngmo Jul 23 '20 at 16:13
  • So instead I should use `MakeDefaultPreset((const char*)"-", kNumPrograms);`? – Josi Whitlock Jul 23 '20 at 16:14
  • No, `MakeDefaultPreset(nullptr, kNumPrograms);` - A sidenote, in the new _iPlug2_ interface, they have changed it to `const char*` so that people don't have to do a pointless cast that scares people like me :-) – Ted Lyngmo Jul 23 '20 at 16:16
  • Ah, okay. The reason I haven't switched to iPlug2 is because the tutorial I'm following uses the old iPlug, and iPlug2 doesn't seem to be finished yet as it says [PRE-RELEASE] on their git page. – Josi Whitlock Jul 23 '20 at 16:18
  • Well, it's a tough choice. Personally I wouldn't spend time learning an old archived framework when there's a new one in the making. It seems it's only pre-release because they haven't shipped their own products with it yet. I'd go for _iPlug2_ for sure. It's actively being developed so you can hope to get support on their github page if anything strange is found. – Ted Lyngmo Jul 23 '20 at 16:24
  • 1
    Yeah, I've decided just to switch frameworks to iPlug2. Thanks for your help! – Josi Whitlock Jul 23 '20 at 16:30
  • You're welcome! ... and I'm looking forward to your VST plugin the next time I fire up Cubase (... it was some 15 years ago I did it last time though ...) :-) – Ted Lyngmo Jul 23 '20 at 16:37

2 Answers2

1

This is what your SynthesizerTwo.cpp file looks like after the pre-processor has included the files, with most cut out:

class SynthesizerTwo : public IPlug
{
...
  double mFrequency;
  void SynthesizerTwo::CreatePresets() {
      
      MakeDefaultPreset((char*)"-", kNumPrograms); //This is what gives me the error
  }
...

const int kNumPrograms = 1; //Original Declaration

As you can see, kNumPrograms is used before it was declared. Declaring it afterwards doesn't help. Besides, if you include "SynthesizerTwo.h" into any other file, there would not be declaration at all.

Solution: Declare variables before their use.

But the thing is, I've tried declaring it in the .h and in the .cpp above the .h's #include. It gave me linker errors, as stated in the OP.

If you define a namespace scope variable in a header, then you should declare it inline.


#define __SYNTHESIZERTWO__

You've defined an identifier that is reserved to the language implementation. As a consequence, the behaviour of your program is undefined (would be if it compiled in the first place). You should use another header guard.

(char*)"-"

This seems dangerous. Avoid casting away constness.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • To clarify the second problem: Names with `__` in them are reserved for the compiler. So maybe one day you compile this on a compiler that defines `__SYNTHESIZERTWO__` by itself, and then your header file suddenly doesn't work. – user253751 Jul 23 '20 at 15:57
  • But the thing is, I've tried declaring it in the `.h` and in the `.cpp` above the `.h`'s `#include`. It gave me linker errors, as stated in the OP. – Josi Whitlock Jul 23 '20 at 15:58
  • Also, I'm using a framework, and the `__` came with it. – Josi Whitlock Jul 23 '20 at 16:00
  • @OboeWanKenobi Are you saying that the framework wrote SynthesizerTwo.h? – eerorika Jul 23 '20 at 16:00
  • The framework came with an example file that showed how to format things, and that was part of it. – Josi Whitlock Jul 23 '20 at 16:02
  • 1
    @OboeWanKenobi Well, now you know that the example that came with the framework is broken. – eerorika Jul 23 '20 at 16:03
  • @eerorika The framework was created years ago and has worked for a ton of people for years. I do realize, however, that it may have changed since it was originally made. – Josi Whitlock Jul 23 '20 at 16:06
  • 1
    @OboeWanKenobi It has been broken in all standard versions of C and C++. If it was written prior to 1989 (standardisation of C), then I suppose the example can be excused (but might be about time to update 30 year old examples to modern standards). `worked for a ton of people for years.` That's the thing with undefined behaviour. Just appearing to work doesn't mean that it works. It could suddenly stop working tomorrow. – eerorika Jul 23 '20 at 16:07
  • @eerorika From what I can tell, it has been maintained, and it was working for me for a while (Three days). – Josi Whitlock Jul 23 '20 at 16:11
  • Well, thanks for the information. I'll have to look into the header guard, as I don't know much about them. – Josi Whitlock Jul 23 '20 at 16:20
  • I'm just going to move to the new Framework. As mentioned by Ted Lyngmo, the new framework does avoid casting away the constness. – Josi Whitlock Jul 23 '20 at 16:27
0

A possible solution is to put extern const int kNumPrograms; in the header before the class.

The extern will work as a forward declaration and the problem will be solved.

for example:

#include <iostream>
//this act like a forward decleration (in your header)
extern const int kNumPrograms;
void f()
{
    std::cout<< kNumPrograms <<std::endl;
}


//real decleration come after (in your cpp)
const int kNumPrograms = 1;

int main()
{
    f();
    return 0;
}
SHR
  • 7,940
  • 9
  • 38
  • 57
  • I'm not sure how to make it reproducible as I'm not sure what exactly is causing the problem besides `SynthesizerTwo.h` not being able to reach kNumPrograms. – Josi Whitlock Jul 23 '20 at 16:15