無理やりiteratorにするとこうなります
試しにスレッドを使ってiteratorも作ってみました。
#include <windows.h> #include <dsound.h> #include <boost/iterator/iterator_facade.hpp> #include <boost/thread/condition.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> #include <string> namespace direct_sound { struct device_info { ::GUID driver_guid; std::string description; std::string module_name; }; namespace detail { class device_info_iterator_impl : boost::noncopyable { enum { request_info, written_info, no_more_info, interrupted }; public: device_info_iterator_impl() : status_(written_info) , thread_(boost::bind(&device_info_iterator_impl::work, this)) { } ~device_info_iterator_impl() { try { { boost::mutex::scoped_lock locking(mutex_); status_ = interrupted; cond_.notify_one(); } thread_.join(); } catch (...) { } } const device_info& dereference() const { return info_; } void increment() { boost::mutex::scoped_lock locking(mutex_); if (status_ == no_more_info) return; status_ = request_info; cond_.notify_one(); while (status_ == request_info) cond_.wait(locking); } bool done() { boost::mutex::scoped_lock locking(mutex_); return status_ == no_more_info; } private: device_info info_; boost::mutex mutex_; boost::condition cond_; volatile int status_; boost::thread thread_; void work() { ::DirectSoundEnumerateA(&callback_func, this); boost::mutex::scoped_lock locking(mutex_); while (status_ == written_info) cond_.wait(locking); status_ = no_more_info; cond_.notify_one(); } ::BOOL callback(::GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule) { boost::mutex::scoped_lock locking(mutex_); while (status_ == written_info) cond_.wait(locking); if (status_ == interrupted) return FALSE; if (lpGuid) info_.driver_guid = *lpGuid; else std::memset(&info_.driver_guid, 0, sizeof(::GUID)); info_.description = lpcstrDescription; info_.module_name = lpcstrModule; status_ = written_info; cond_.notify_one(); return TRUE; } static ::BOOL CALLBACK callback_func( ::GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) { return static_cast<device_info_iterator_impl*>(lpContext) ->callback(lpGuid, lpcstrDescription, lpcstrModule); } }; } // namespace detail class device_info_iterator : public boost::iterator_facade< device_info_iterator, const device_info, boost::single_pass_traversal_tag > { friend class boost::iterator_core_access; typedef detail::device_info_iterator_impl impl_type; public: // end()用 device_info_iterator() { } // begin()用、引数はダミー explicit device_info_iterator(int) : pimpl_(new impl_type) { increment(); } private: boost::shared_ptr<impl_type> pimpl_; reference dereference() const { return pimpl_->dereference(); } void increment() { pimpl_->increment(); if (pimpl_->done()) pimpl_.reset(); } bool equal(const device_info_iterator& rhs) const { return pimpl_ == rhs.pimpl_; } }; } // namespace direct_sound // お試し #include <algorithm> #include <iostream> struct print_device_name { void operator()(const direct_sound::device_info& info) const { std::cout << info.description << std::endl; } }; int main() { std::for_each( direct_sound::device_info_iterator(0), direct_sound::device_info_iterator(), print_device_name()); }
Fiberで実装すれば、多少軽くなるかもしれません。
↓ディレクトリ探索で似たことをやっている人がいました。
http://blogs.msdn.com/oldnewthing/archive/2004/12/29/343664.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/12/30/344281.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx
http://blogs.msdn.com/oldnewthing/archive/2005/01/03/345800.aspx
http://blogs.msdn.com/oldnewthing/archive/2005/01/04/346274.aspx