mpd: make Iterator more flexible

This commit is contained in:
Andrzej Rybczak
2014-11-02 22:52:13 +01:00
parent 0457af36fe
commit afa8a34340
2 changed files with 132 additions and 74 deletions

View File

@@ -29,6 +29,58 @@
MPD::Connection Mpd; MPD::Connection Mpd;
namespace {
bool fetchItem(MPD::ItemIterator::State &state)
{
auto src = mpd_recv_entity(state.connection());
if (src != nullptr)
{
state.setObject(src);
return true;
}
else
return false;
}
bool fetchPlaylist(MPD::PlaylistIterator::State &state)
{
auto src = mpd_recv_playlist(state.connection());
if (src != nullptr)
{
state.setObject(src);
return true;
}
else
return false;
}
bool fetchSong(MPD::SongIterator::State &state)
{
auto src = mpd_recv_song(state.connection());
if (src != nullptr)
{
state.setObject(src);
return true;
}
else
return false;
}
bool fetchOutput(MPD::OutputIterator::State &state)
{
auto src = mpd_recv_output(state.connection());
if (src != nullptr)
{
state.setObject(src);
return true;
}
else
return false;
}
}
namespace MPD { namespace MPD {
Connection::Connection() : m_connection(nullptr), Connection::Connection() : m_connection(nullptr),
@@ -295,7 +347,7 @@ SongIterator Connection::GetPlaylistChanges(unsigned version)
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_queue_changes_meta(m_connection.get(), version); mpd_send_queue_changes_meta(m_connection.get(), version);
checkErrors(); checkErrors();
return SongIterator(m_connection.get(), mpd_recv_song); return SongIterator(m_connection.get(), fetchSong);
} }
Song Connection::GetCurrentSong() Song Connection::GetCurrentSong()
@@ -322,7 +374,7 @@ SongIterator Connection::GetPlaylistContent(const std::string &path)
{ {
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_playlist_meta(m_connection.get(), path.c_str()); mpd_send_list_playlist_meta(m_connection.get(), path.c_str());
SongIterator result(m_connection.get(), mpd_recv_song); SongIterator result(m_connection.get(), fetchSong);
checkErrors(); checkErrors();
return result; return result;
} }
@@ -331,7 +383,7 @@ SongIterator Connection::GetPlaylistContentNoInfo(const std::string &path)
{ {
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_playlist(m_connection.get(), path.c_str()); mpd_send_list_playlist(m_connection.get(), path.c_str());
SongIterator result(m_connection.get(), mpd_recv_song); SongIterator result(m_connection.get(), fetchSong);
checkErrors(); checkErrors();
return result; return result;
} }
@@ -609,7 +661,7 @@ PlaylistIterator Connection::GetPlaylists()
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_playlists(m_connection.get()); mpd_send_list_playlists(m_connection.get());
checkErrors(); checkErrors();
return PlaylistIterator(m_connection.get(), mpd_recv_playlist); return PlaylistIterator(m_connection.get(), fetchPlaylist);
} }
void Connection::GetList(mpd_tag_type type, StringConsumer f) void Connection::GetList(mpd_tag_type type, StringConsumer f)
@@ -661,7 +713,7 @@ SongIterator Connection::CommitSearchSongs()
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_search_commit(m_connection.get()); mpd_search_commit(m_connection.get());
checkErrors(); checkErrors();
return SongIterator(m_connection.get(), mpd_recv_song); return SongIterator(m_connection.get(), fetchSong);
} }
void Connection::CommitSearchTags(StringConsumer f) void Connection::CommitSearchTags(StringConsumer f)
@@ -682,7 +734,7 @@ ItemIterator Connection::GetDirectory(const std::string &directory)
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_meta(m_connection.get(), directory.c_str()); mpd_send_list_meta(m_connection.get(), directory.c_str());
checkErrors(); checkErrors();
return ItemIterator(m_connection.get(), mpd_recv_entity); return ItemIterator(m_connection.get(), fetchItem);
} }
ItemIterator Connection::GetDirectoryRecursive(const std::string &directory) ItemIterator Connection::GetDirectoryRecursive(const std::string &directory)
@@ -690,7 +742,7 @@ ItemIterator Connection::GetDirectoryRecursive(const std::string &directory)
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_all_meta(m_connection.get(), directory.c_str()); mpd_send_list_all_meta(m_connection.get(), directory.c_str());
checkErrors(); checkErrors();
return ItemIterator(m_connection.get(), mpd_recv_entity); return ItemIterator(m_connection.get(), fetchItem);
} }
void Connection::GetDirectories(const std::string &directory, StringConsumer f) void Connection::GetDirectories(const std::string &directory, StringConsumer f)
@@ -711,7 +763,7 @@ SongIterator Connection::GetSongs(const std::string &directory)
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_list_meta(m_connection.get(), directory.c_str()); mpd_send_list_meta(m_connection.get(), directory.c_str());
checkErrors(); checkErrors();
return SongIterator(m_connection.get(), mpd_recv_song); return SongIterator(m_connection.get(), fetchSong);
} }
OutputIterator Connection::GetOutputs() OutputIterator Connection::GetOutputs()
@@ -719,7 +771,7 @@ OutputIterator Connection::GetOutputs()
prechecksNoCommandsList(); prechecksNoCommandsList();
mpd_send_outputs(m_connection.get()); mpd_send_outputs(m_connection.get());
checkErrors(); checkErrors();
return OutputIterator(m_connection.get(), mpd_recv_output); return OutputIterator(m_connection.get(), fetchOutput);
} }
void Connection::EnableOutput(int id) void Connection::EnableOutput(int id)

View File

@@ -329,16 +329,75 @@ typedef std::vector<Item> ItemList;
typedef std::vector<std::string> StringList; typedef std::vector<std::string> StringList;
typedef std::vector<Output> OutputList; typedef std::vector<Output> OutputList;
template <typename DestT, typename SourceT> template <typename ObjectT>
struct Iterator: std::iterator<std::input_iterator_tag, DestT> struct Iterator: std::iterator<std::input_iterator_tag, ObjectT>
{ {
typedef SourceT *(*SourceFetcher)(mpd_connection *); // shared state of the iterator
struct State
{
friend Iterator;
typedef bool (*Fetcher)(State &);
State(mpd_connection *connection, Fetcher fetcher)
: m_connection(connection)
, m_fetcher(fetcher)
{
assert(m_connection != nullptr);
assert(m_fetcher != nullptr);
}
~State()
{
mpd_response_finish(m_connection);
}
mpd_connection *connection() const
{
return m_connection;
}
void setObject(ObjectT object)
{
if (hasObject())
*m_object = std::move(object);
else
m_object.reset(new ObjectT(std::move(object)));
}
private:
bool operator==(const State &rhs) const
{
return m_connection == rhs.m_connection
&& m_object == m_object;
}
bool operator!=(const State &rhs) const
{
return !(*this == rhs);
}
bool fetch()
{
return m_fetcher(*this);
}
ObjectT &getObject() const
{
return *m_object;
}
bool hasObject() const
{
return m_object.get() != nullptr;
}
mpd_connection *m_connection;
Fetcher m_fetcher;
std::unique_ptr<ObjectT> m_object;
};
Iterator() Iterator()
: m_state(nullptr) : m_state(nullptr)
{ } { }
Iterator(mpd_connection *connection, SourceFetcher fetch_source) Iterator(mpd_connection *connection, typename State::Fetcher fetcher)
: m_state(std::make_shared<State>(connection, fetch_source)) : m_state(std::make_shared<State>(connection, std::move(fetcher)))
{ {
// get the first element // get the first element
++*this; ++*this;
@@ -350,14 +409,14 @@ struct Iterator: std::iterator<std::input_iterator_tag, DestT>
m_state = nullptr; m_state = nullptr;
} }
DestT &operator*() const ObjectT &operator*() const
{ {
if (!m_state) if (!m_state)
throw std::runtime_error("no object associated with the iterator"); throw std::runtime_error("no object associated with the iterator");
assert(m_state->hasObject()); assert(m_state->hasObject());
return m_state->getObject(); return m_state->getObject();
} }
DestT *operator->() const ObjectT *operator->() const
{ {
return &**this; return &**this;
} }
@@ -365,10 +424,7 @@ struct Iterator: std::iterator<std::input_iterator_tag, DestT>
Iterator &operator++() Iterator &operator++()
{ {
assert(m_state); assert(m_state);
auto src = m_state->fetchSource(); if (!m_state->fetch())
if (src != nullptr)
m_state->setObject(src);
else
finish(); finish();
return *this; return *this;
} }
@@ -389,63 +445,13 @@ struct Iterator: std::iterator<std::input_iterator_tag, DestT>
} }
private: private:
struct State
{
State(mpd_connection *conn, SourceFetcher fetch_source)
: m_connection(conn)
, m_fetch_source(fetch_source)
{
assert(m_connection != nullptr);
assert(m_fetch_source != nullptr);
}
~State()
{
mpd_response_finish(m_connection);
}
bool operator==(const State &rhs) const
{
return m_connection == rhs.m_connection
&& m_object == m_object;
}
bool operator!=(const State &rhs) const
{
return !(*this == rhs);
}
SourceT *fetchSource() const
{
return m_fetch_source(m_connection);
}
DestT &getObject() const
{
return *m_object;
}
bool hasObject() const
{
return m_object.get() != nullptr;
}
void setObject(DestT object)
{
if (hasObject())
*m_object = std::move(object);
else
m_object.reset(new DestT(std::move(object)));
}
private:
mpd_connection *m_connection;
SourceFetcher m_fetch_source;
std::unique_ptr<DestT> m_object;
};
std::shared_ptr<State> m_state; std::shared_ptr<State> m_state;
}; };
typedef Iterator<Item, mpd_entity> ItemIterator; typedef Iterator<Item> ItemIterator;
typedef Iterator<Output, mpd_output> OutputIterator; typedef Iterator<Output> OutputIterator;
typedef Iterator<Playlist, mpd_playlist> PlaylistIterator; typedef Iterator<Playlist> PlaylistIterator;
typedef Iterator<Song, mpd_song> SongIterator; typedef Iterator<Song> SongIterator;
class Connection class Connection
{ {