mag
Интересующийся
Offline
Пол:
|
|
« : 01-11-2010 08:00 » |
|
Дамы и господа,
Есть ли какой-нибудь законный (или не очень законный) способ узнать, что виртуальная функция с определённой сигнатурой будет расположена в таблице виртуальных функций под таким-то номером?
Речь не о том, чтобы вызвать функцию, а именно в том, чтобы узнать номер.
|
|
« Последнее редактирование: 01-11-2010 08:14 от Алексей1153++ »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #1 : 01-11-2010 08:28 » |
|
mag, это было бы как-то так struct A { virtual void F1() { }
virtual void F2() { }
bool GetVirtFuncNumber(const void* pF,DWORD& number_out) { number_out=0; const void** __vfprt=(const void**)this; DWORD maxCount=sizeof(*this)/sizeof(*__vfprt); for(DWORD dwd=0;dwd<maxCount;dwd++) { if(__vfprt[dwd]==pF) { number_out=dwd; return true; } }
return false; } };
но тут сложность - как передать указатель на функцию, которая не статическая ) Я не осилил... А всё-таки, зачем это нужно ? Где это может пригодиться ?
|
|
« Последнее редактирование: 01-11-2010 08:30 от Алексей1153++ »
|
Записан
|
|
|
|
Dale
|
|
« Ответ #2 : 01-11-2010 08:57 » |
|
Это нужно узнать динамически во время выполнения?
Вообще по факту вроде бы они располагаются в порядке объявления в интерфейсе. Хотя это не оговорено в стандартах, но именно так обычно ведут себя реализации для Windows, и на этом основывается COM.
|
|
« Последнее редактирование: 01-11-2010 09:00 от Dale »
|
Записан
|
Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.
Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard
Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #3 : 01-11-2010 12:12 » |
|
to Алексей1153++ А как всё это вызвать? У меня A a; bool bTest = a.GetVirtFuncNumber(&A::F2,number_out); в VS2008 выдаёт ошибку error C2664: 'A::GetVirtFuncNumber' : cannot convert parameter 1 from 'void (__thiscall A::* )(void)' to 'const void *' to Dale >> Это нужно узнать динамически во время выполнения? Устроит любой вариант: при компиляции (линковке) либо во время выполнения (см. чуть ниже, где описывается проблема для чего всё это нужно). Первый даже предпочтительнее, но на него я не претендую. >> Вообще по факту вроде бы они располагаются в порядке объявления в интерфейсе. Хотя это не оговорено в стандартах, но именно так обычно ведут себя реализации для Windows, и на этом основывается COM И я так думал, пока... Итак, проблема. Некоторое время мы пользовались следующим неявным соглашением (не хочу обсуждать чем оно лучше-хуже технологии COM или использования наследования при декларировании интерфейсов). При расширении интерфейса мы добавляли новые виртуальные функции только в конце. class IStart // версия 1 { public: virtual void F0 () = 0; virtual void F1 () = 0; };
Через некоторое время мы расширяли интерфейс таким образом class IStart // версия 2 { public: virtual void F0 () = 0; virtual void F1 () = 0; virtual void F2 () = 0; };
Кроме этого была возможность понять к какой именно версии интерфейса относится данная реализация. Это позволяло более поздним версиям клиентов при получении более ранних реализаций просто не вызывать более поздние функции (не спрашивайте меня о последствиях, если всё-таки вызовет). Компилятор действительно, располагал в виртуальной таблице все функции в порядке их объявления и всё прекрасно работало. До тех пор, пока мы не перегрузили одну из функций. class IStart // версия 3 { public: virtual void F0 () = 0; virtual void F1 () = 0; virtual void F2 () = 0; virtual void F1 (int) = 0; };
Оказывается, в виртуальной таблице перегруженные функции появляются в обратном порядке, при этом непергруженные имена идут в том же порядке, что и раньше. А именно для версии 3 это было F0(), F1(int),F1(),F2). Вот тут-то меня и встретили две бессонные ночи накануне выпуска релиза. Хочется в приёмочных тестах написать несколько тестов, которые будут проверять весь интерфейс на порядок следования функций...
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #4 : 01-11-2010 12:28 » |
|
mag, как вызвать - я не знаю. Я об этом написал. Подозреваю, что никак
Добавлено через 16 минут и 12 секунд: mag, может быть, выходом будет не перегружать, а задать другое имя.
|
|
« Последнее редактирование: 01-11-2010 12:46 от Алексей1153++ »
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #5 : 01-11-2010 13:04 » |
|
Это я теперь знаю, что нельзя перегружать функции из разных версий, причём внутри одной версии их вполне можно перегружать. Но порою мне не хватает образования и знания языков, чтобы придумать разным функциям разные имена, особенно в ситуации когда они делают одно и тоже, но отличаются списком аргументов.
Для решения основной задачи пробовал пользоваться указателями на функции классов вида void (IStart:: pFunc*)() = &IStart::F0, но они к сожалению, указывают на не адреса функций, прописанных в __vptr, а на место, которое подготавливает аргументы и вызывает функции из виртуальной таблицы.
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Dale
|
|
« Ответ #6 : 01-11-2010 13:13 » |
|
Подозреваю, что никак. Вполне логично. Потому что результат преобразования типа void (__thiscall A::* )(void) в const void * непонятен.
|
|
|
Записан
|
Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.
Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard
Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #7 : 01-11-2010 14:35 » |
|
mag, Не совсем понятно для чего это нужно.
Для работы непосредственно с функциями класса Я бы посмотрел бы как работает класс функтор. Там не заморачиваются на нижнеуровневом исполнении. А используют специфику именно С++.
В атаче пример из библиотеки Loki.
|
|
« Последнее редактирование: 01-11-2010 14:44 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #8 : 01-11-2010 15:29 » |
|
Но порою мне не хватает образования и знания языков, чтобы придумать разным функциям разные имена, особенно в ситуации когда они делают одно и тоже, но отличаются списком аргументов.
ну, тут всё просто. Приписываешь уточнение: GetValue();//первый вариант GetValue_byPointer(void* ptr);//2 вариант GetValue_ToString(syd::string& result);//3 вариант
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #9 : 16-11-2010 09:22 » |
|
Если кому интересно, проблему удалось частично решить используя следующую технологию. С помощью макросов создаётся класс, в котором для каждой функции исходного интерфейса делается две функции. Одна для формального определения функции интерфейса (которая никогда не вызывается) а другая для проверки индекса данной виртуальной функции. // Исходный интерфейс class IStart // версия 1 { public: virtual void F0 () = 0; virtual void F1 (int) = 0; // ... };
// Тестирующий класс class CTest_IStart : public IStart { public: void F0 () { } bool _OnTest_F0 (int nIndex);
void F1 (int) { } bool _OnTest_F1 (int nIndex,int); // ... };
В реализации каждой тестирующей функции создаётся производный от CTest_IStart, в котором переопределена только одна эта функция class CTest_IStart : public IStart { // ... bool _OnTest_F1 (int nIndex,int) { class I1 : public CTest_IStart { void F1 (int) { } }; I1 i1; return PVF(this,nIndex) != PVF (&i1,nIndex); // PVF(this,nIndex) - макрос, возвращающий указатель на функцию в виртуальной таблице }
Таким образом, создав объект CTest_IStart и вызвав у него _OnTest_F1 (1,(int)0) можно проверить, что у класса CTest_IStart::_OnTest_F1::I1 изменена именно эта функция в таблице виртуальных функций. Остались некоторые вопросы. 1) Можно ли узнать количество виртуальных функций у интерфейса ? А то для большого индекса, выходящего за пределы количества виртуальных функций область памяти там расположенная наверняка различна. 2) Как заставить это работать в релизной сборке программы? Беда в том, что оптимизатор подставляет инлайновую реализацию функций и проверка не проходит.
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #10 : 16-11-2010 09:30 » |
|
всё, что не должно быть оптимизировано, нужно пометить volatile
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #11 : 16-11-2010 09:35 » |
|
1) спросить размер поделить на размер указателя и вычесть 1 2) а метод описанный Алексей1153++ чем плох Вообще ваша проблема лечится другим способом надо запретить меня интерфейс новые функции не добавлять в один интерфейсный, а наследоваться от него и добавлять в наследника. вот ваш пример после переработки: class IStartV1 // версия 1 { public: virtual void F0 () = 0; virtual void F1 () = 0; }; class IStartV2 : public IStartV1 // версия 2 { public: virtual void F2 () = 0; };
class IStartV3 : public IStartV2 // версия 3 { public: virtual void F1 (int) = 0; };
можно еще поизголятся разными способами, но суть не меняется, ранее выданные соглашения по интерфейсу менять нельзя например class IStartV1 // версия 1 { public: virtual void F0 () = 0; virtual void F1 () = 0; };
class IStartV2 // версия 2 { public: virtual void F0 () = 0; virtual void F1 () = 0; virtual void F2 () = 0; };
class IStartV3 // версия 2 { public: virtual void F0 () = 0; virtual void F1 () = 0; virtual void F2 () = 0; virtual void F0 (int) = 0; };
|
|
|
Записан
|
Странно всё это....
|
|
|
npak
|
|
« Ответ #12 : 16-11-2010 09:40 » |
|
mag, не понятно, что вы хотите добиться. В С++ есть поддержка Runtime Type Information. При помощи оператора динамического приведения типов dynamic_cast вы можете привести указатель с нужному вам интерфейсу. Если такое приведение невозможно, то dynamic_cast выбросит исключение std::bad_cast. Это честный способ вызывать виртуальные функции - рантайм сам скажет, допустима операция или нет. Поддержка RTTI в MS VC включается опцией /GR http://msdn.microsoft.com/en-us/library/cby9kycs.aspx
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #13 : 16-11-2010 09:47 » |
|
to Алексей1153++ А в каком месте этот volatile нужно поставить? В названии функции не могу, так как это изменяет её сигнатуру.
to Антон (LogRus) >> 1) спросить размер поделить на размер указателя и вычесть 1 Размер чего? Класса?
>> Вообще ваша проблема лечится другим способом Я же писал ранее, что не хочу вдаваться в подробности, чем такая схема лучше или хуже технологии COM, где запрещено менять имеющийся интерфейс. Например, нужно писать разные функции по запросу данных интерфейсов (грубо говоря, Get_V1 , Get_V2 или через QueryInterface с разными идентификаторами), в функцтях клиентах данных интерфейсов тоже изменять сигнатуру (а далеко не все они нуждаются в новых функциях) и т.д..
Добавлено через 3 минуты и 57 секунд: to npak RTTI не годится. Так как классы, реализующие данные интерфейсы реализованы в разных библиотеках и собраны без применения RTTI. Соответственно, изменить это я не могу.
|
|
« Последнее редактирование: 16-11-2010 09:51 от mag »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Антон (LogRus)
|
|
« Ответ #14 : 16-11-2010 10:19 » |
|
>> 1) спросить размер поделить на размер указателя и вычесть 1 Размер чего? Класса?
>> Вообще ваша проблема лечится другим способом Я же писал ранее, что не хочу вдаваться в подробности, чем такая схема лучше или хуже технологии COM, где запрещено менять имеющийся интерфейс. Например, нужно писать разные функции по запросу данных интерфейсов (грубо говоря, Get_V1 , Get_V2 или через QueryInterface с разными идентификаторами), в функцтях клиентах данных интерфейсов тоже изменять сигнатуру (а далеко не все они нуждаются в новых функциях) и т.д..
1) Интерфейса, хотя наверное тут я нагнал, так вероятно не получится т.к. интерфейс не хранит в себе таблицу виртуальных функций, а только указатель 2) про ком не говорил, я говорил про работу без грязных платформозависимых хаков
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #15 : 16-11-2010 10:38 » |
|
А в каком месте этот volatile нужно поставить? В названии функции не могу, так как это изменяет её сигнатуру.
перед функцией поставить. Сигнатура не измениться. Для того и ставится, чтобы вообще ничего компилятор не изменил во время оптимизации
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #16 : 16-11-2010 11:19 » |
|
to Алексей1153++ >> перед функцией поставить. Сигнатура не измениться. Для того и ставится, чтобы вообще ничего компилятор не изменил во время оптимизации Насколько я понял по сообщениям компилятора варианты volatile virtual void F0 () {} virtual volatile void F0 () {} virtual void volatile F0 () {}
переопрделяют возвращаемое значение функции, а virtual void F0 () volatile {} изменяет её сигнатуру и неприемлем в данной ситуации to Антон (LogRus) >> 2) про ком не говорил, я говорил про работу без грязных платформозависимых хаков Сорри. Про COM и варианты использования наследования при декларировании интерфейсов писал я. В том контесте, что не хочу обсуждать их применимость или неприменимость в данном случае. А что касается хаков, так данная проверка мне нужна не для работы и вызовов этих функций с не очень корректными преобразованиями а лишь для приёмочных тестов и проверки того, что кто-то случайно не испортил интерфейс.
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #17 : 16-11-2010 11:28 » |
|
вот такой вариант правильный
volatile virtual void F0 () {}
а какие сообщения выдал компилятор ?
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #18 : 16-11-2010 11:39 » |
|
А что касается хаков, так данная проверка мне нужна не для работы и вызовов этих функций с не очень корректными преобразованиями а лишь для приёмочных тестов и проверки того, что кто-то случайно не испортил интерфейс.
на мой взгляд это ничего не меняет, в любом случае это скорее идеалогический спор нежли практический, на мой взгляд тест могбы отнаследоваься от интерфейса и подёргать заглушки т.е. одна часть теста скопилирована давно и работает со старым интерфейсом, другая просто заглушка которая собарана с новым, первая часть подгружает вторую и дёргает её методы делов на 15 минут, зачем тут танцы с бубном мне всё равно не ясно
|
|
« Последнее редактирование: 16-11-2010 11:46 от Антон (LogRus) »
|
Записан
|
Странно всё это....
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #19 : 16-11-2010 11:46 » |
|
>> а какие сообщения выдал компилятор ?
Для первых трёх случаев: error C2555: 'CTest_IStart::F0': overriding virtual function return type differs and is not covariant from 'CTest_IStart::F0'
а последний: error C2259: 'CTest_IStart::_Test_F0::I1' : cannot instantiate abstract class due to following members: 'void IStart::F0 ()' : is abstract. see declaration of 'IStart::F0'
Добавлено через 8 минут и 7 секунд: to Антон (LogRus)
Я как раз и не хочу обсуждать другие варианты и переводить обсуждение в идеологический спор. Интерфейс (и способ его изменения) есть де-факто и мне с ним работать. Если честно, я не очень понял последнего предложения о заглушках. Можно подробнее.
|
|
« Последнее редактирование: 16-11-2010 11:54 от mag »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #20 : 16-11-2010 11:54 » |
|
mag, хм, даже не знаю, что там такое. Послушаем мнение более опытных ))
А у меня подозрение, что volatile только для переменных работает, а для функций я никогда не использовал
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #21 : 16-11-2010 11:59 » |
|
to Антон (LogRus)
Я как раз и не хочу обсуждать другие варианты и переводить обсуждение в идеологический спор. Интерфейс (и способ его изменения) есть де-факто и мне с ним работать. Если честно, я не очень понял последнего предложения о заглушках. Можно подробнее.
Собственно в последнем посте я не предлагал менять интерфейс, я предложил вариант теста который не требует хаков и решает задачу. Идиология текущих интерфейсов тут не затрагивается. При этом трудоёмкость реализации теста минимальна и платформонезависима, ну и главное строго в рамках стандарта C++. Добавлено через 4 минуты и 15 секунд:mag, хм, даже не знаю, что там такое. Послушаем мнение более опытных ))
А у меня подозрение, что volatile только для переменных работает, а для функций я никогда не использовал
ничего там такого 1. говорит, что пытаешься перегрузить функцию по возвращаемому значению, что запрещено 2. говорит вам, что вы создали второй метод в наследнике, а дали опеределеление родительского тут volatile работает также как и const
|
|
« Последнее редактирование: 16-11-2010 12:04 от LogRus »
|
Записан
|
Странно всё это....
|
|
|
npak
|
|
« Ответ #22 : 16-11-2010 12:07 » |
|
Возвращаюсь к исходному вопросу. Есть ли какой-нибудь законный (или не очень законный) способ узнать, что виртуальная функция с определённой сигнатурой будет расположена в таблице виртуальных функций под таким-то номером?
Проблема в том, что в общем случае в объекте хранятся несколько таблиц виртуальных функций из-за наследования. Вот пример: #include <stdio.h> class Base { public: int x; virtual void F(); }; class BaseA : virtual public Base { public: int y; virtual void F_A();}; class BaseB : virtual public Base { public: int z; virtual void F_B();};
void Base::F() {} void BaseA::F_A() { } void BaseB::F_B() { }
class Derived : public BaseA, public BaseB {};
int main() { Derived d; d.x = 0xbacebace; d.y = 0xaaabacea; d.z = 0xbbbbaceb; Derived* pD = &d; BaseA* pA = pD; BaseB* pB = pD; Base * p = pD; Base * pBaseA = pA; Base * pBaseB = pB;
printf("Dervied at %p, BaseA at %p, BaseB at %p\n", pD, pA, pB); printf("Base of Dervied at %p, Base of BaseA at %p, Base of BaseB at %p\n", p, pBaseA, pBaseB);
printf("Size of Derived: %d\n", sizeof(Derived)); printf ("Derived memory layout:\n"); unsigned int * words = *(unsigned**)&pD; for (int i = 0; i < sizeof(Derived)/sizeof(unsigned); i++) { printf("%p\t0x%08x\n", words+i, words[i]); } return 0; } Я поддерживаю предложение Антона - создать статически наследников от каждого из классов и узнавать адреса методов у них средствами компилятора. Это не общее решение, так как рассчитано на конкретный набор классов, зато не требует никаких реализационно-зависимых хаков
|
|
« Последнее редактирование: 16-11-2010 12:15 от npak »
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #23 : 16-11-2010 12:18 » |
|
to npak >> Проблема в том, что в общем случае в объекте хранятся несколько таблиц виртуальных функций из-за наследования
Это одна из причин, по которой был выбран вариант, в котором при объявлении интерфейсов не используется наследование. Один интерфейс - одна виртуальная таблица. И из-за того, что поддержка COM основывается на таком-же принципе, врядли разработчики компиляторов решаться это сломать.
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Антон (LogRus)
|
|
« Ответ #24 : 16-11-2010 18:04 » |
|
mag, ты мое сообщение читал. Чем плох мой вариант?
|
|
|
Записан
|
Странно всё это....
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #25 : 17-11-2010 09:10 » |
|
Антон (LogRus), читать-то я его читал, но как сразу не понял, так и дополнительных комментариев мне не хватило.
>> Идиология текущих интерфейсов тут не затрагивается. Это значит, что рассматривается мой вариант, когда в новых версиях функции дописываются в конце?
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #26 : 17-11-2010 10:49 » |
|
А при решении какой задачи может понадобиться такая информация? Самописный механизм Reflections для C++?
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Антон (LogRus)
|
|
« Ответ #27 : 17-11-2010 17:10 » |
|
Антон (LogRus), читать-то я его читал, но как сразу не понял, так и дополнительных комментариев мне не хватило.
>> Идиология текущих интерфейсов тут не затрагивается. Это значит, что рассматривается мой вариант, когда в новых версиях функции дописываются в конце?
да, интефейсы реализуй как тебе нравится, хоть периодически функции местами переставляй самый простой вариант релизации: 0. Тест работает так: наследуемся от интрефейса и реализуем заглушки для каждой из функций, пускай заглушки пишут в консоль имя функции (со всеми типами и именами, можно макрос для упрощения задачи), далее тест поочереди дёргает каждый из методов заглушки, естетственно через интерфейс на базовый тип 1. Собираем тест до изменения интерфейса, запускаем и получаем лог 2. Собираем тест после изменений, запускаем и получаем лог 3. Сравниваем 2 лога, если всё совпали то считаем этот тест эталонным старый переименовываем и сохраняем гденибуть еще, ну так вдруг пригодится (в тесте важно добавлять дёргали, в конец списка, а то логи замучаешься сравнивать) 4. Проверяем что тест заваливается, если поменять порядок функций в интерфейсе возможно имеет смысл разделить каждый из тестов на две части DLL + EXE, тогда схемка немного другая для испытаний, но зато не надо парится над порядком вызовов в тесте и вообще кажется, что так более правильно 1. запускаем старый тест со старой DLL 2. запускаем старый тест с новой DLL 3. сравниваем логи 4. если порядок, старый тест заменяем на новый. ну порядок действий можно накручивать и оптимизировать, например, тест может фиксировать результаты в SVN для истории самого себя туда же думаю суть идеи понятна
|
|
|
Записан
|
Странно всё это....
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #28 : 20-11-2010 10:28 » |
|
Антон (LogRus), >> самый простой вариант релизации:... >> 4. Проверяем что тест заваливается, если поменять порядок функций в интерфейсе
Вот это как раз и проблема. Если я сам буду приписывать функциям номера (или что тоже самое буду сам управлять порядком их вызова) то я не увижу никаких изменений в логе теста, если в исходном интерфейсе порядок функций будет каким-то образом изменён. Представь ситуацию с перегруженной функцией. Её добавили в конце существующего набора функций, в тестах вызвали в конце и в логе она появилась в конце. Тест работает, а интерфейс нет.
Второй вариант более правильный. Как мне кажется, небольшой его недостаток в том, что функции интерфейса всё-же вызываются. И если их порядок изменён, то это может приводить либо к проблемам в передаче параметров, либо к вызову функции по произвольному или нулевому адресу. Но в общем, это приемлемо для приёмочных тестов.
|
|
« Последнее редактирование: 20-11-2010 18:14 от Алексей1153++ »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Антон (LogRus)
|
|
« Ответ #29 : 20-11-2010 18:12 » |
|
Второй вариант лучше не по этому, а потому что он моделирует реальные условия. судя по первому предложению ты до конца не понял идею.
Ты уверен в именах в своём посте? Извините если что. Тяжело с мобильного писать.
|
|
|
Записан
|
Странно всё это....
|
|
|
|