/* A random access file iterator, and a main function showing its use in std::binary_search. See also: http://wordaligned.org/articles/using-boost-to-eliminate-iterator-boilerplate My thanks to Giuseppe for fixing this code! http://disq.us/gb6aj */ #include #include #include #include #include #include #include #include #include // File read error, thrown when low level file access fails. class file_read_error : public std::runtime_error { public: file_read_error(std::string const & what) : std::runtime_error(what) { } }; template class text_file_iter : public boost::iterator_facade< text_file_iter , value , std::random_access_iterator_tag , value , std::streamoff > { typedef text_file_iter iter; private: // Sanity // Check things are OK, throwing an error on failure. void check(bool ok, std::string const & what) const { if (!ok) { throw file_read_error(what); } } public: enum start_pos { begin, end }; public: // Lifecycle text_file_iter(iter const & other) : fname(other.fname) { open(); setpos(other.pos); } text_file_iter() : pos(-1) { } text_file_iter(std::string const & fname, start_pos where = begin) : fname(fname) , pos(-1) { open(); if (where == end) { seek_end(); } } ~text_file_iter() { close(); } iter & operator=(iter const & other) { close(); fname = other.fname; open(); setpos(other.pos); return *this; } private: // Everything Boost's iterator facade needs friend class boost::iterator_core_access; value dereference() const { return read(); } bool equal(iter const & other) const { return pos == other.pos; } void increment() { advance(1); } void decrement() { advance(-1); } void advance(std::streamoff n) { check(in.seekg(n, std::ios_base::cur), "advance failed"); pos = getpos(); } std::streamoff distance_to(iter const & other) const { return other.pos - pos; } public: // Allow direct access to the underlying stream position std::streampos getpos() { std::streampos pos_ = in.tellg(); check(in, "getpos failed"); return pos_; } private: // Implementation details void open() { in.open(fname.c_str(), std::ios::binary); check(in, "open failed"); pos = getpos(); } void close() { if (in.is_open()) { in.close(); check(in, "close failed"); } } void seek_end() { check(in.seekg(0, std::ios_base::end), "seek_end failed"); chop_whitespace(); pos = getpos(); } void chop_whitespace() { do { in.unget(); } while (isspace(in.peek())); in.get(); in.clear(); } void setpos(std::streampos newpos) { check(in.seekg(newpos), "setpos failed"); pos = newpos; } // Return the item at the current position value read() const { value n; // Reverse till we hit whitespace or the start of the file while (in && !isspace(in.peek())) { in.unget(); } in.clear(); check(in >> n, "read failed"); return n; } private: // State std::string fname; mutable std::ifstream in; std::streampos pos; }; /* Accepts a number and the name of a sorted text file of numbers passed in on the command line. Binary searches the file for the number, returning 0 if found, 1 otherwise. */ int main(int argc, char * argv[]) { typedef long long number; typedef text_file_iter iter; number v; std::stringstream s; s << argv[1]; s >> v; iter b(argv[2], iter::begin); iter e(argv[2], iter::end); return std::binary_search(b, e, v) ? 0 : 1; }