2017-09-05 85 views
2

我对C++和一般编译语言非常陌生我有一个强烈的解释背景,所以我试图让我的头在编译时无法访问某些东西的限制。将代码块优化为循环

目前我有一大块代码看起来像这样:

//New note, note36 

MemoryInputStream* input36 = new MemoryInputStream(BinaryData::C1hard1_wav, BinaryData::C1hard1_wavSize, false); 
auto reader36 = audioFormatManager.createReaderFor(input36); 

BigInteger note36; 
note36.setRange(36, 1, true); 

addSound(new SamplerSound("note36", *reader36, note36, 36, 0.001, 0.01, 26.0)); 

delete input36; 
delete reader36; 

//New note, note37 

MemoryInputStream* input37 = new MemoryInputStream(BinaryData::Csharp1hard1_wav, BinaryData::Csharp1hard1_wavSize, false); 
auto reader37 = audioFormatManager.createReaderFor(input37); 

BigInteger note37; 
note37.setRange(37, 1, true); 

addSound(new SamplerSound("note37", *reader37, note37, 37, 0.001, 0.01, 26.0)); 

delete input37; 
delete reader37; 

此代码重复48次在该方法中,这是不好的实践。我将如何在PHP中实现这样的一个例子,解释型语言会是这个样子

$noteMap = array(36 => "C1", 37 => "Csharp1"); 

foreach($noteMap as $midiNumber => $note) 
{ 
    $name = $note . "hard1_wav"; 
    $size = $note . "hard1_wavSize"; 

    $input = new MemoryInputStream(BinaryData::$name, BinaryData::$size, false); 
    $reader = audioFormatManager->createReaderFor($input); 

    //I know this bit is bad PHP, no bigintegers in PHP but I can't think of a way to replicate it for arguments sake 
    $note = 0; 
    $note->setRange($midiNumber, 1, true); 

    addSound(new SamplerSound("note".$midiNumber, $reader36, $note, $midiNumber, 0.001, 0.01, 26.0)); 
} 

这是更易于管理和重用。我所做的更改无需在整个文件中仔细重复,并且可以快速进行更改。我知道将变量作为函数名传递并不是在编译语言中完成的,但即使考虑到这一点,也必须有一种方法将我的大代码块包装成一个整洁的循环,但我无法找到解释的资源它对我来说。

+1

你不能在C++中做到这一点。变量名称在运行时不可修改。代替阅读器和MIDI通道,使用'std :: vector'或其他容器并循环播放。 – user0042

+1

在C++中,您可以对地图使用'std :: map',对'foreach'使用'for'范围。而且您不必为'input'或'reader'使用低效的动态分配。 –

+1

@ user0042:他的代码不需要运行时变量名称。 –

回答

1

假设BinaryData是一个JUCE生成的资源文件,下面的算法应该等同于您的PHP版本。这不使用动态变量名称,而是使用应出现在JUCE generated code中的BinaryData::getNamedResource函数。

std::map<int, std::string> note_map{{36, "C1"}, {37, "Csharp1"}}; 
for (const auto& note : note_map) { 
    BigInteger midi_bit; 
    midi_bit.setBit(note.first); 

    int size; 
    std::string note_resource = note.second + "hard1_wav"; 
    auto data = BinaryData::getNamedResource(note_resource.c_str(), size); 

    auto input = new MemoryInputStream(data, size, false); 
    auto reader = audioFormatManager->createReaderFor(input); 

    auto note_name = "note" + std::to_string(note.first); 
    addSound(new SamplerSound(note_name.c_str(), *reader, midi_bit, 
           note.first, 0.001, 0.01, 26.0)); 
} 

另一个(坏)的解决方案是使用C预处理器来生成代码类似如下:

#define note(num, name)                        \ 
    MemoryInputStream* input##num = new MemoryInputStream(BinaryData::name##_wav, BinaryData::name##_wavSize, false); \ 
    auto reader##num = audioFormatManager.createReaderFor(input##num);            \ 
    BigInteger note##num;                        \ 
    note##num.setBit(num);                       \ 
    addSound(new SamplerSound("note" #num, *reader##num, note##num, num, 0.001, 0.01, 26.0));       \ 
    delete input##num;                        \ 
    delete reader##num 
note(36, C1); 
note(37, Csharp1); 

下面的代码是由预处理阶段产生:

MemoryInputStream* input36 = new MemoryInputStream(BinaryData::C1_wav, BinaryData::C1_wavSize, false); auto reader36 = audioFormatManager.createReaderFor(input36); BigInteger note36; note36.setBit(36); addSound(new SamplerSound("note" "36", *reader36, note36, 36, 0.001, 0.01, 26.0)); delete input36; delete reader36; 
MemoryInputStream* input37 = new MemoryInputStream(BinaryData::Csharp1_wav, BinaryData::Csharp1_wavSize, false); auto reader37 = audioFormatManager.createReaderFor(input37); BigInteger note37; note37.setBit(37); addSound(new SamplerSound("note" "37", *reader37, note37, 37, 0.001, 0.01, 26.0)); delete input37; delete reader37; 

预处理器解决方案应该是与当前解决方案等效的代码,但通常以这种方式使用宏生成大量变量并不是一个好习惯。上述运行时解决方案应优于使用预处理器。

+0

看起来就是JUCE框架代码。我不认为有JUCE的具体答案,所以我没有在我原来的问题中提到它。你的答案似乎是围绕我所需要的,但是就目前的形式而言,它会导致我的主机应用程序崩溃,即使它编译得很好! – Richie

+1

我想这是因为我忘记了注释名称的“_wav”部分。我会编辑我的答案,试图包含这一点。你可以'#include '并使用'assert(data)'来确保'getNamedResource'返回的指针不是'nullptr'。 – Jonesinator

+1

我不熟悉JUCE执行内存管理的方式。如果JUCE不承担存储器的所有权,您可能需要在循环中'删除'用'new'创建的项目。我没有在循环中包含'delete'语句,但它们都在您的原始代码中。 – Jonesinator

0

不要说,这样的资源浪费,你在哪里调用新的和删除每个注释。

改为使用数据结构,例如std::vectorstd::map

一旦你填充你的数据结构,你可以迭代它的元素。

+0

这只是一个数组,没有数据只在这些值中的字符串。这些字符串然后被解释为方法名称,我相信这是在C++中完全不可能的东西,所以我不确定这里的解决方案是什么 – Richie

+0

@Richie,对不对! – gsamaras

1

你可以做这样的事情:

typedef void (FuncPtr)(int x); 
void C1Func(int x); 
void CSharpFunc(int x); 

void someFunc() { 
    const Input input = new MemoryInputStream(name, size, false); 
    const Reader reader = audioFormatManager->createReaderFor(input); 

    // note: gxx syntax here, may not be supported in other versions...  
    Note noteMap[48] = { [36]=Note(36,C1Func), [37]=Note(37,CSharpFunc) }; 
    String name, size; 

    for (int i = 0; i < arraySizeOf(noteMap); i++) { 
     Note * note = noteMap[i]; 
     if (!note->isInitialized()) 
      continue; 
     name = note + "hard1_wav"; 
     size = note + "hard1_wavSize"; 
     note->setRange(i,1,true); 
     std::string name = "note"+i; 
     addSound(new SamplerSound(name, reader, note[i], i, 0.001, 0.01, 26.0)); 
    } 
    delete input; 
    delete reader; 
} 

你需要一个Note类构造函数(Note::Note(int idx,FuncPtr fn))。此外,数组的初始化特定于gnu,因此您可能需要使用std::map或其他相同的东西(或将INVALID_NOTE定义为无效,并在其中添加一堆INVALID_NOTE,以将其他数组成员标记为空)。