У меня есть код C ++ (написанный кем-то другим), который, похоже, вызывает неправильную функцию. Вот ситуация:
UTF8InputStreamFromBuffer* cstream = foo();
wstring fn = L"foo";
DocumentReader* reader;
if (a_condition_true_for_some_files_false_for_others) {
reader = (DocumentReader*) _new GoodDocumentReader();
} else {
reader = (DocumentReader*) _new BadDocumentReader();
}
// the crash happens inside the following call
// when a BadDocumentReader is used
doc = reader->readDocument(*cstream, fn);
Файлы, для которых выполняется условие, обрабатываются нормально; те, для которых это ложный сбой. Иерархия классов для DocumentReader выглядит следующим образом:
class GenericDocumentReader {
virtual Document* readDocument(InputStream &strm, const wchar_t * filename) = 0;
}
class DocumentReader : public GenericDocumentReader {
virtual Document* readDocument(InputStream &strm, const wchar_t * filename) {
// some stuff
}
};
class GoodDocumentReader : public DocumentReader {
Document* readDocument(InputStream & strm, const wchar_t * filename);
}
class BadDocumentReader : public DocumentReader {
virtual Document* readDocument(InputStream &stream, const wchar_t * filename);
virtual Document* readDocument(const LocatedString *source, const wchar_t * filename);
virtual Document* readDocument(const LocatedString *source, const wchar_t * filename, Symbol inputType);
}
Следующее также имеет значение:
class UTF8InputStreamFromBuffer : public wistringstream {
// foo
};
typedef std::basic_istream<wchar_t> InputStream;
Запуск в отладчике Visual C ++ показывает, что вызов readDocument в BadDocumentReader вызывает не
readDocument(InputStream&, const wchar_t*)
, а скорее
readDocument(const LocatedString* source, const wchar_t *, Symbol)
. подтверждается наклеиванием инструкций cout во все прочитанные документы. После вызова исходный аргумент, конечно, заполнен мусором, который вскоре вызывает сбой. У LocationString есть неявный конструктор с одним аргументом из InputStream, но проверка с помощью cout показывает, что он не вызывается. Есть идеи, чем это объяснить?
Edit : другие, возможно, важные детали: классы DocumentReader находятся в другой библиотеке, чем вызывающий код. Я также полностью перестроил весь код, и проблема осталась.
Редактировать 2 : Я использую Visual C ++ 2008.
Редактировать 3 : Я попытался сделать "минимально компилируемый пример" "с тем же поведением, но не смог воспроизвести проблему.
Edit 4 :
По предложению Билли ONeal я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мое подозрение, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : Классы DocumentReader находятся в другой библиотеке, чем вызывающий код. Я также полностью перестроил весь код, и проблема осталась.
Редактировать 2 : Я использую Visual C ++ 2008.
Редактировать 3 : Я попытался сделать "минимально компилируемый пример" "с тем же поведением, но не смог воспроизвести проблему.
Редактировать 4 :
По предложению Билли Онила я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мое подозрение, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : Классы DocumentReader находятся в другой библиотеке, чем вызывающий код. Я также полностью перестроил весь код, и проблема осталась.
Редактировать 2 : Я использую Visual C ++ 2008.
Редактировать 3 : Я попытался сделать "минимально компилируемый пример" "с тем же поведением, но не смог воспроизвести проблему.
Редактировать 4 :
По предложению Билли Онила я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мои подозрения, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : Я также полностью перестроил весь код, и проблема осталась.
Редактировать 2 : Я использую Visual C ++ 2008.
Редактировать 3 : Я попытался сделать "минимально компилируемый пример" "с тем же поведением, но не смог воспроизвести проблему.
Редактировать 4 :
По предложению Билли Онила я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мое подозрение, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : Я также полностью перестроил весь код, и проблема осталась.
Редактировать 2 : Я использую Visual C ++ 2008.
Редактировать 3 : Я попытался сделать "минимально компилируемый пример" "с тем же поведением, но не смог воспроизвести проблему.
Редактировать 4 :
По предложению Билли Онила я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мои подозрения, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : с тем же поведением, но не смог воспроизвести проблему.
Edit 4 :
По предложению Билли ONeal я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мое подозрение, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : с тем же поведением, но не смог воспроизвести проблему.
Edit 4 :
По предложению Билли ONeal я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Конечно, когда я меняю порядок, он меняет вызываемую функцию. Мне кажется, это подтверждает мое подозрение, что с индексацией в vtable происходит что-то странное, но я не уверен, что это вызывает.
Редактировать 5 : Вот разборка нескольких строк перед вызовом функции:
00559728 mov edx,dword ptr [reader]
0055972E mov eax,dword ptr [edx]
00559730 mov ecx,dword ptr [reader]
00559736 mov edx,dword ptr [eax]
00559738 call edx
Я не очень разбираюсь в сборке, но мне кажется, что это разыменование указателя переменной считывателя. Первым, что хранится в этой части памяти, должен быть указатель на vtable, поэтому он разыменовывает его в eax. Затем он помещает первый элемент в vtable в edx и вызывает его. Перекомпиляция с разными порядками методов, похоже, этого не меняет. Он всегда хочет вызвать первое, что есть в vtable. (Я мог совершенно неправильно это понять, вообще не имея представления о сборке ...)
Спасибо за вашу помощь.
Редактировать 6: Я обнаружил проблему и прошу прощения за зря потраченное время. Проблема заключалась в том, что GoodDocumentReader должен был быть объявлен как подкласс DocumentReader, но на самом деле это не так. Приведение в стиле C подавляло ошибку компилятора (должен был прислушаться к вам, @sellibitze, если вы хотите отправить свой комментарий в качестве ответа, я отмечу его как правильный). Сложность заключается в том, что код работал несколько месяцев по чистой случайности, пока не появилась ревизия, когда кто-то добавил еще две виртуальные функции в GoodDocumentReader, так что он больше не вызывал нужную функцию по счастливой случайности.