Совместно с этим вопросом . У меня возникли проблемы с поиском хорошего типа безопасного решения следующей, казалось бы, основной проблемы. У меня есть класс music_playlist, в котором есть список песен, которые он должен проигрывать. Кажется довольно просто, просто создайте std :: список всех песен в очереди и сделайте его доступным для пользователя. Однако из-за необходимости декодирование аудио и рендеринг аудио происходит в отдельных потоках. Таким образом, список должен быть защищен мьютексом. Часто мьютекс был забыт другими программистами, использующими мою библиотеку. Что, очевидно, привело к «странным» проблемам.
Итак, сначала я просто написал сеттеры для класса.
struct music{};
class music_playlist{
private:
std::list<music> playlist;
public:
void add_song(music &song){playlist.push_back(song);}
void clear(){playlist.clear();}
void set_list(std::list<music> &songs){playlist.assign(songs.begin(),songs.end());}
//etc
};
Это привело к коду пользователя, как показано ниже ...
music song1;
music song2;
music song3;
music song4;
music song5;
music song6;
music_playlist playlist1;
playlist1.add_song(song1);
playlist1.add_song(song2);
playlist1.add_song(song3);
playlist1.add_song(song4);
playlist1.add_song(song5);
playlist1.add_song(song6);
//or
music_playlist playlist2;
std::list<music> songs;
songs.push_back(song1);
songs.push_back(song2);
songs.push_back(song3);
songs.push_back(song3);
songs.push_back(song5);
songs.push_back(song6);
playlist2.set_list(songs);
Пока это работает и очень явно. Это очень утомительно печатать, и это подвержено ошибкам из-за всей суеты вокруг фактической работы. Чтобы продемонстрировать это, я на самом деле намеренно вставил ошибку в приведенный выше код, что-то вроде этого было бы легко сделать и, вероятно, прошло бы нетронутые обзоры кода, в то время как song4 никогда не воспроизводится в списке воспроизведения 2.
Оттуда я пошел изучать функции с переменными числами.
struct music{};
class music_playlist{
private:
std::list<music> playlist;
public:
void set_listA(music *first,...){
//Not guaranteed to work, but usually does... bleh
va_list Arguments;
va_start(Arguments, first);
if (first) {
playlist.push_back(first);
}
while (first) {
music * a = va_arg(Arguments, music*);
if (a) {
playlist.push_back(a);
}else {
break;
}
}
}
void set_listB(int count,music first,...){
va_list Arguments;
va_start(Arguments, first);
playlist.push_back(first);
while (--count) {
music a = va_arg(Arguments, music);
playlist.push_back(a);
}
}
//etc
};
Что приведет к пользовательскому коду, как показано ниже ...
playlist1.set_listA(&song1,&song2,&song3,&song4,&song5,&song6,NULL);
//playlist1.set_listA(&song1,&song2,&song3,&song4,&song5,&song6); runtime error!!
//or
playlist2.set_listB(6,song1,song2,song3,song4,song5,song6);
Теперь гораздо легче увидеть, была ли песня добавлена дважды или не включена. Однако в решении A произойдет сбой, если NULL не находится в конце списка и не является кроссплатформенным. В SolutionB вы должны посчитать количество аргументов, которые могут привести к нескольким ошибкам. Кроме того, ни одно из решений не является безопасным по типу, и пользователь может передать несвязанный тип и ожидать сбой во время выполнения. Это не казалось устойчивым решением. И вот я наткнулся на std :: initializer_list. Не могу использовать несколько компиляторов, для которых я разрабатываю, пока что их нет. Поэтому я попытался подражать этому. Я закончил с этим решением ниже.
Это использование оператора "," считается плохим тоном?
Что приведет к тому, что код пользователя будет выглядеть следующим образом ...
struct music_playlist{
list<music> queue;
//...
};
int main (int argc, const char * argv[])
{
music_playlist playlist;
music song1;
music song2;
music song3;
music song4;
playlist.queue = song1,song2; // The queue now contains song1 and song2
playlist.queue+= song1,song3,song4; //The queue now contains two song1s and song2-4
playlist.queue = song2; //the queue now only contains song2
return 0;
}
Этот синтаксис не смутил нашу маленькую тестовую группу. Однако у меня были серьезные опасения по поводу злоупотребления перегрузкой оператора. Поэтому я разместил вопрос выше. Я хотел посмотреть, что думают программисты, которые были сравнительно экспертами для нашей тестовой группы. Многим программистам это не понравилось, однако оно показалось лучше, чем приведенные выше решения, потому что оно отлавливало бы большинство ошибок во время компиляции, а не только во время выполнения. Однако Том опубликовал интересный встречный пример, который может вызвать неожиданное поведение.
//lets combine differnt playlists
new_playlist.queue = song1 //the first playlist
,(song3,song4) //the second playlist //opps, I didn't add song 3!
, song5;
Это подрывает меня к этому решению. Итак, у вас есть идеи по поводу лучшего решения?
P.S. Ни один из приведенных выше кодов не был скомпилирован, они просто приведены для примера.