(Моя push_back
утилита описана внизу.)
typedef std::vector<std::string> Block;
int main() {
using namespace std;
vector<Block> blocks;
string const end = "end";
// no real difference from using ifstream, btw
for (fstream file ("filename", file.in); file;) {
Block& block = push_back(blocks);
for (string line; getline(file, line);) {
if (line == end) {
break;
}
push_back(block).swap(line);
}
if (!file && block.empty()) {
// no lines read, block is a dummy not represented in the file
blocks.pop_back();
}
}
return 0;
}
Пример сериализации:
template<class OutIter>
void bencode_block(Block const& block, OutIter dest) {
int len = 0;
for (Block::const_iterator i = block.begin(); i != block.end(); ++i) {
len += i->size() + 1; // include newline
}
*dest++ = len;
*dest++ = ':';
for (Block::const_iterator i = block.begin(); i != block.end(); ++i) {
*dest++ = *i;
*dest++ = '\n';
}
}
Я использовал простой формат кодирования сериализации. Пример подходящего выходного итератора, который просто записывает в поток:
struct WriteStream {
std::ostream& out;
WriteStream(std::ostream& out) : out(out) {}
WriteStream& operator++() { return *this; }
WriteStream& operator++(int) { return *this; }
WriteStream& operator*() { return *this; }
template<class T>
void operator=(T const& value) {
out << value;
}
};
Пример использования:
bencode_block(block, WriteStream(std::cout));
Другой возможный выходной итератор, который записывает в дескриптор файла (например, сетевой сокет):
struct WriteFD {
int out;
WriteFD(int out) : out(out) {}
WriteFD& operator++() { return *this; }
WriteFD& operator++(int) { return *this; }
WriteFD& operator*() { return *this; }
template<class T>
void operator=(T const& value) {
if (write(value) == -1) {
throw std::runtime_error(strerror(errno));
}
}
//NOTE: write methods don't currently handle writing less bytes than provided
int write(char value) {
return write(out, &value, 1);
}
int write(std::string const& value) {
return write(out, value.data(), value.size());
}
int write(int value) {
char buf[20];
// handles INT_MAX up to 9999999999999999999
// handles INT_MIN down to -999999999999999999
// that's 19 and 18 nines, respectively (you did count, right? :P)
int len = sprintf(buf, "%d", value);
return write(out, buf, len);
}
};
Семантика движения бедного человека:
template<class C>
typename C::value_type& push_back(C& container) {
container.push_back(typename C::value_type());
return container.back();
}
Это позволяет легко использовать семантику перемещения, чтобы избежать ненужных копий:
container.push_back(value); // copies
// becomes:
// (C is the type of container)
container.push_back(C::value_type()); // add empty
container.back().swap(value); // swap contents