====== Eliminating char[] arrays ====== Perhaps the biggest weakness of the C programming language is that it has no built-in character-string type. As a results, large chunks of the [[wp>C_standard_library|C Standard Library]] exist to implement strings using contiguous blocks of ''char''(either arrays or //malloc()//-allocated chunks on the heap), taking advantage of the fact that the old ASCII code didn't assign a meaning to an all-zeros bit pattern, so an ASCII ''NUL'' could be used to terminate "strings" of arbitrary length. This, of course, has resulted in countless man-years of debugging frustration since 1972. I recall reading somewhere that at least one famous computer scientist suggested that C++ was one giant attempt to fix C's lack of a string type. Unfortunately, C++ didn't have an "official" character-string type either, and because it was originally designed as a proper superset of C, programmers (myself included) kept on using the nasty old null-terminated ''char'' array techniques, including all those traditional C Standard Library functions like ''strcpy'', ''memcpy'', ''memset'', and so on. Why? Because it worked, it was reasonably efficient, and the number of available C++ string classes was so bewildering (''std::string'', added with the [[wp>C++_Standard_Library|C++ Standard Library]], being just one more heaped onto the bonfire) that many people just preferred to stick with what they knew. JUCE includes a perfectly acceptable new //String// class, which is used widely and highly consistently throughout the entire library, so it makes good sense to use it for all strings in JUCE programs. ===== Application in the VanillaJuce code ===== My original implementation of the //SynthParameters// class was essentially a ''struct'' which included a fixed-length ''char[]'' array for the program name, like this: #define kMaxProgramNameLength 24 class SynthParameters { public: char programName[1 + kMaxProgramNameLength]; // 1 extra byte for null terminator ... }; Yes (he said guiltily), I really did use an archaic C ''#define'' for the array length. I checked the old VST2.4 SDK to find the maximum program-name length, and dutifully added one more ''char'' for the null. I did this because my initial implementation of //VanillaJuceAudioProcessor::getStateInformation()// etc. actually used ''memcpy()'' to make a binary copy the whole parameter-block, and because the //VanillaJuceAudioProcessor//'s ''programBank'' member variable is a simple array (also sized using a ''#define''!), so I needed a fixed block size. Now that I have switched over to using JUCE's very nice XML classes for serializing patch data, the fixed block size isn't as important. Moreover, since the //juce::AudioProcessor::getName()// function is defined to return a //juce::String// (presumably truncating it to 24 characters for VST2.4 hosts), I don't even have to worry about the maximum length. Hence it's a no-brainer to us a //juce::String// for the program name, allowing me to delete the ''#define'': class SynthParameters { public: String programName; ... }; The resulting code changes elsewhere, to eliminate clumsy uses of things like ''sprintf()'' and //juce::String::copyToUTF8()//, etc. are too obvious (and embarrassing) to even list here. ===== Use of "const char*" for string literals ===== I do still use the ''char'' type in a very specific way in **VanillaJuce**, to declare a static array of string literals in ''SynthWaveform.cpp'': const char* const SynthWaveform::wfNames[] = { "Sine", "Triangle", "Square", "Sawtooth" }; As Jules pointed out in his list of code issues, it's acceptable and preferable to use string literals (which the C++ compiler will interpret as being of type ''const char*'') to initialize //juce::String// variables, and in place of //juce::String//-typed function parameters. This is because //juce::String// has been defined to include a constructor which takes a ''const char*'' argument, and therefore, the C++ compiler will silently invoke this constructor function to turn the given literal string into a //juce::String// instance automatically. In terms of the above table of waveform names, I could have made it an array of //juce::String// objects, but this would be redundant. Ordinarily, I get uneasy whenever the C++ compiler silently inserts function calls in this way, because it obscures the flow of control. In this case I'm OK with it, because it is a well-established JUCE programming convention. ===== Eliminating unnecessary String() constructor invocations ===== A perfect example of why it's a good idea to avoid writing ''String("xyz")'' and just use the literal ''"xyz"'' arose in some code I used in //SynthParameters::getXml()//, which is a series of function calls like this: xml->setAttribute(String("masterLevel"), masterLevel); I had used the ''String("xyz")'' construct simply because I didn't know much about JUCE's XML functions, and had copy-pasted the code from an example I found elsewhere. When I looked, though, I noticed that the //juce::XmlElement::setAttribute()// function is actually declared as void XmlElement::setAttribute (const Identifier& attributeName, const double number) along with many related definitions, for different data-types in the second argument. In every case, however, the first argument is not a //juce::String// at all, but something called a //juce::Identifier//. Presumably, the C++ compiler is accepting my //juce::String// arguments by silently invoking an appropriate //juce::Identifier// constructor I wasn't even aware of. Depending how good its optimization is, this might be quite inefficient. The lesson is clear: whenever possible, use simple string literals, and trust that the JUCE library has defined appropriate constructors to perform whatever conversions may be necessary. All the ''setAttribute'' calls in my //SynthParameters::getXml()// implementation now look like this: xml->setAttribute("masterLevel", masterLevel);