2017-09-05 110 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,以將其他數組成員標記爲空)。