2014-11-22 3 views
1

Я пытаюсь использовать gzstream 1.5 для разработки ios под xcode 6.1, libz.1.dylib.gzstream lib открытие существующего файла

Эта библиотека была написана довольно давно.

Я обнаружил, что

class igzstream : public gzstreambase, public std::istream 

должен быть

class igzstream : public gzstreambase, public virtual std::istream 

То же самое для ogzstream.

Потому что, если файл не существует, первый вариант возвращает true для good() после инициализации. AFAIK это из-за двух предков std :: ios.

Интересно, действительно ли это ошибка, и почему она еще не исправлена!

ответ

2

стандарт C++ определяет имя std::istream, как ЬурейеЕ из std::basic_istream<char> в [lib.iostream.format], который фактически полученный из std::basic_ios<char>, в соответствии с [lib.istream]. С другой стороны, gzstreambase фактически получен из std::ios, который определен в [lib.iostream.forward] как typedef std::basic_ios<char>. Таким образом, обе ветви наследования имеют отношение виртуального наследования к std::ios (aka std::basic_ios<char>).

Если ваша стандартная реализация библиотеки не нарушена, вы не должны получать два подобъекта std::ios в igzstream, но объявление виртуального базового класса имеет дополнительные последствия, изменяя порядок инициализации базового класса.

class igzstream : public gzstreambase, public std::istream

Виртуальные базовые классы (даже косвенные) инициализируются первый, так std::ios сначала должны быть инициализированы, который в свою очередь инициализирует std::ios_base (не-виртуального базового класса сам по себе). Затем не виртуальные базовые классы инициализируются в порядке слева направо, поэтому сначала gzstreambase, затем std::istream.

class igzstream : public gzstreambase, virtual public std::istream

Виртуальные базовые классы (даже косвенные) инициализируются первый, так std::ios сначала должны быть инициализированы, который в свою очередь инициализирует std::ios_base (не-виртуального базового класса сам по себе). Затем инициализируется std::istream, поскольку он по-прежнему является еще одним виртуальным базовым классом, но ему необходимо std::ios и, наконец, gzstreambase.

Имея это в виду, вы можете определить, что виртуальный вывод из std::istream кажется очень плохая идея, потому что конструктор igzstream передает адрес своего члена gzstreambuf под названием ЬиЕ к конструктору std::istream объекта до того, как инициализирован унаследованный элемент buf.

Возможно, причиной вашей проблемы является то, что gzstreambase(consth char *, int) вызовы std::ios::init(), а std::istream конструктор ведет себя как если бы он делает то же самое, в соответствии с [lib.istream.cons]. Функция std::ios::init документирована для инициализации потока в хорошем состоянии. Поэтому, если субобъект istream инициализируется после объекта gzstreambase, второй init базового объекта ios должен действительно очищать флаги ошибок. Это действительно похоже на ошибку в библиотеке gzstream.Правильный порядок построения (gzstreambuf first, istream second, а затем попытка открыть файл) кажется совершенно нетривиальной проблемой. В исходной версии есть «gzstreambuf, open, istream», где istream сжимает открытый сбой, в то время как ваше предлагаемое исправление имеет «istream, gzstreambuf, open», где istream получает адрес еще не сконструированного streambuf.

Обходной путь заключается в том, чтобы не использовать конструктор открытия gzstream, но я подумаю о хорошем решении для исправления открытия конструктора и отредактирую ответ, как только приду к результату.

В зависимости от того, кого вы запрашиваете, несколько вызовов инициализации в порядке (обычная интерпретация http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#135) или не определены (http://article.gmane.org/gmane.comp.lib.boost.devel/235659). В компиляторе Microsoft многократный вызов init вызывает утечку памяти, а Dinkumware (кто предоставляет библиотеку ввода-вывода, используемую Microsoft) настаивает на том, что стандарт не определяет поведение при нескольких вызовах, поэтому это неопределенное поведение.

Так что для практического портативного поведения не вызывайте init повторно. Но это то, что происходит в gzstream. На самом деле это одна из ситуаций, когда противники множественного наследования, как на C++, кажутся правильными. You do необходимо наследовать std :: istream, чтобы иметь возможность «istream interface», а с другой стороны, вам нужно не наследовать std :: istream, потому что его конструктор делает то, что вам не нужно , Если std :: istream были «просто интерфейсом», вы могли бы реализовать его наряду с реализацией реализации из gzstreambase без проблем.

Единственное решение, которое я вижу в этом случае, - это удаление конструктора gzstreambase, выполняющего открытый, и помещение открытых вызовов в конструкторы igzstream и ogzstream (таким образом, дублирование вызова для открытия). Таким образом, можно полагаться на init, который вызывается один раз и только один раз в конструкторе istream/ostream.

+0

Майкл снова здесь! Полное объяснение. Спасибо! – Erdemus

Смежные вопросы