Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: Номер виртуальной функции в таблице виртуальных функций  (Прочитано 45280 раз)
0 Пользователей и 8 Гостей смотрят эту тему.
mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« : 01-11-2010 08:00 » 

Дамы и господа,

Есть ли какой-нибудь законный (или не очень законный) способ узнать, что виртуальная функция с определённой сигнатурой будет расположена в таблице виртуальных функций под таким-то номером?

Речь не о том, чтобы вызвать функцию, а именно в том, чтобы узнать номер.
« Последнее редактирование: 01-11-2010 08:14 от Алексей1153++ » Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline 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
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 01-11-2010 08:57 » 

Это нужно узнать динамически во время выполнения?

Вообще по факту вроде бы они располагаются в порядке объявления в интерфейсе. Хотя это не оговорено в стандартах, но именно так обычно ведут себя реализации для Windows, и на этом основывается COM.
« Последнее редактирование: 01-11-2010 09:00 от Dale » Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
mag
Интересующийся

ru
Offline 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). Вот тут-то меня и  встретили две бессонные ночи накануне выпуска релиза. Хочется в приёмочных тестах написать несколько тестов, которые будут проверять весь интерфейс на порядок следования функций...
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 01-11-2010 12:28 » new

mag, как вызвать - я не знаю. Я об этом написал. Подозреваю, что никак

Добавлено через 16 минут и 12 секунд:
mag, может быть, выходом будет не перегружать, а задать другое имя.
« Последнее редактирование: 01-11-2010 12:46 от Алексей1153++ » Записан

mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 01-11-2010 13:04 » 

Это я теперь знаю, что нельзя перегружать функции из разных версий, причём внутри одной версии их вполне можно перегружать. Но порою мне не хватает образования и знания языков, чтобы придумать разным функциям разные имена, особенно в ситуации когда они делают одно и тоже, но отличаются списком аргументов.

Для решения основной задачи пробовал пользоваться указателями на функции классов вида void (IStart:: pFunc*)() = &IStart::F0, но они к сожалению, указывают на не адреса функций, прописанных в __vptr, а на место, которое подготавливает аргументы и вызывает функции из виртуальной таблицы.
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 01-11-2010 13:13 » 

Подозреваю, что никак.

Вполне логично. Потому что результат преобразования типа void (__thiscall A::* )(void) в const void * непонятен.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #7 : 01-11-2010 14:35 » 

mag, Не совсем понятно для чего это нужно.

Для работы непосредственно с функциями класса Я бы посмотрел бы как работает класс функтор. Там не заморачиваются на нижнеуровневом исполнении. А используют специфику именно С++.

В атаче пример из библиотеки Loki.

* Functor.h (73.35 Кб - загружено 830 раз.)
« Последнее редактирование: 01-11-2010 14:44 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 01-11-2010 15:29 » 

Но порою мне не хватает образования и знания языков, чтобы придумать разным функциям разные имена, особенно в ситуации когда они делают одно и тоже, но отличаются списком аргументов.
ну, тут всё просто. Приписываешь уточнение:
Код:
GetValue();//первый вариант
GetValue_byPointer(void* ptr);//2 вариант
GetValue_ToString(syd::string& result);//3 вариант
Записан

mag
Интересующийся

ru
Offline 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) Как заставить это работать в релизной сборке программы? Беда в том, что оптимизатор подставляет инлайновую реализацию функций и проверка не проходит.
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #10 : 16-11-2010 09:30 » 

всё, что не должно быть оптимизировано, нужно пометить volatile
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #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
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
mag
Интересующийся

ru
Offline 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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #14 : 16-11-2010 10:19 » 

>> 1) спросить размер поделить на размер указателя и вычесть 1
Размер чего? Класса?

>> Вообще ваша проблема лечится другим способом
Я же писал ранее, что не хочу вдаваться в подробности, чем такая схема лучше или хуже технологии COM, где запрещено менять имеющийся интерфейс. Например, нужно писать разные функции по запросу данных интерфейсов (грубо говоря, Get_V1 , Get_V2 или через QueryInterface с разными идентификаторами), в функцтях клиентах данных интерфейсов тоже изменять сигнатуру (а далеко не все они нуждаются в новых функциях) и т.д..
1) Интерфейса, хотя наверное тут я нагнал, так вероятно не получится т.к. интерфейс не хранит в себе таблицу виртуальных функций, а только указатель
2) про ком не говорил, я говорил про работу без грязных платформозависимых хаков
Записан

Странно всё это....
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 16-11-2010 10:38 » 

А в каком месте этот volatile нужно поставить? В названии функции не могу, так как это изменяет её сигнатуру.
перед функцией поставить. Сигнатура не измениться. Для того и ставится, чтобы вообще ничего компилятор не изменил во время оптимизации

Записан

mag
Интересующийся

ru
Offline 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 и варианты использования наследования при декларировании интерфейсов писал я. В том контесте, что не хочу обсуждать их применимость или неприменимость в данном случае.
А что касается хаков, так данная проверка мне нужна не для работы и вызовов этих функций с не очень корректными преобразованиями а лишь для приёмочных тестов и проверки того, что кто-то случайно не испортил интерфейс.
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 16-11-2010 11:28 » 

вот такой вариант правильный

volatile virtual void F0 () {}

а какие сообщения выдал компилятор ?
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #18 : 16-11-2010 11:39 » 

А что касается хаков, так данная проверка мне нужна не для работы и вызовов этих функций с не очень корректными преобразованиями а лишь для приёмочных тестов и проверки того, что кто-то случайно не испортил интерфейс.
на мой взгляд это ничего не меняет, в любом случае это скорее идеалогический спор нежли практический, на мой взгляд тест могбы отнаследоваься от интерфейса и подёргать заглушки
т.е. одна часть теста скопилирована давно и работает со старым интерфейсом, другая просто заглушка которая собарана с новым, первая часть подгружает вторую и дёргает её методы делов на 15 минут, зачем тут танцы с бубном мне всё равно не ясно
« Последнее редактирование: 16-11-2010 11:46 от Антон (LogRus) » Записан

Странно всё это....
mag
Интересующийся

ru
Offline 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 » Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #20 : 16-11-2010 11:54 » 

mag, хм, даже не знаю, что там такое. Послушаем мнение более опытных ))


А у меня подозрение, что volatile только для переменных работает, а для функций я никогда не использовал
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #21 : 16-11-2010 11:59 » 

to Антон (LogRus)

Я как раз и не хочу обсуждать другие варианты и переводить обсуждение в идеологический спор. Интерфейс (и способ его изменения) есть де-факто и мне с ним работать.
Если честно, я не очень понял последнего предложения о заглушках. Можно подробнее.
Собственно в последнем посте я не предлагал менять интерфейс, я предложил вариант теста который не требует хаков и решает задачу. Идиология текущих интерфейсов тут не затрагивается.
При этом трудоёмкость реализации теста минимальна и платформонезависима, ну и главное строго в рамках стандарта C++.

Добавлено через 4 минуты и 15 секунд:
mag, хм, даже не знаю, что там такое. Послушаем мнение более опытных ))


А у меня подозрение, что volatile только для переменных работает, а для функций я никогда не использовал

ничего там такого
1. говорит, что пытаешься перегрузить функцию по возвращаемому значению, что запрещено
2. говорит вам, что вы создали второй метод в наследнике, а дали опеределеление родительского тут volatile работает также как и const 
« Последнее редактирование: 16-11-2010 12:04 от LogRus » Записан

Странно всё это....
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #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 » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #23 : 16-11-2010 12:18 » 

to npak
>> Проблема в том, что в общем случае в объекте хранятся несколько таблиц виртуальных функций из-за наследования

Это одна из причин, по которой был выбран вариант, в котором при объявлении интерфейсов не используется наследование. Один интерфейс - одна виртуальная таблица. И из-за того, что поддержка COM основывается на таком-же принципе, врядли разработчики компиляторов решаться это сломать.
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #24 : 16-11-2010 18:04 » 

mag, ты мое сообщение читал. Чем плох мой вариант?
Записан

Странно всё это....
mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 17-11-2010 09:10 » 

Антон (LogRus), читать-то я его читал, но как сразу не понял, так и дополнительных комментариев мне не хватило.

>> Идиология текущих интерфейсов тут не затрагивается.
Это значит, что рассматривается мой вариант, когда в новых версиях функции дописываются в конце?
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #26 : 17-11-2010 10:49 » 

А при решении какой задачи может понадобиться такая информация? Самописный механизм Reflections для C++?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #27 : 17-11-2010 17:10 » 

Антон (LogRus), читать-то я его читал, но как сразу не понял, так и дополнительных комментариев мне не хватило.

>> Идиология текущих интерфейсов тут не затрагивается.
Это значит, что рассматривается мой вариант, когда в новых версиях функции дописываются в конце?

да, интефейсы реализуй как тебе нравится, хоть периодически функции местами переставляй

самый простой вариант релизации:
0. Тест работает так: наследуемся от интрефейса и реализуем заглушки для каждой из функций, пускай заглушки пишут в консоль имя функции (со всеми типами и именами, можно макрос для упрощения задачи), далее тест поочереди дёргает каждый из методов заглушки, естетственно через интерфейс на базовый тип
1. Собираем тест до изменения интерфейса, запускаем и получаем лог
2. Собираем тест после изменений, запускаем и получаем лог
3. Сравниваем 2 лога, если всё совпали то считаем этот тест эталонным старый переименовываем и сохраняем гденибуть еще, ну так вдруг пригодится (в тесте важно добавлять дёргали, в конец списка, а то логи замучаешься сравнивать)
4. Проверяем что тест заваливается, если поменять порядок функций в интерфейсе

возможно имеет смысл разделить каждый из тестов на две части DLL + EXE, тогда схемка немного другая для испытаний, но зато не надо парится над порядком вызовов в тесте и вообще кажется, что так более правильно
1. запускаем старый тест со старой DLL
2. запускаем старый тест с новой DLL
3. сравниваем логи
4. если порядок, старый тест заменяем на новый.

ну порядок действий можно накручивать и оптимизировать, например, тест может фиксировать результаты в SVN для истории самого себя туда же

думаю суть идеи понятна
Записан

Странно всё это....
mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #28 : 20-11-2010 10:28 » 

Антон (LogRus),
>> самый простой вариант релизации:...
>> 4. Проверяем что тест заваливается, если поменять порядок функций в интерфейсе

Вот это как раз и проблема. Если я сам буду приписывать функциям номера (или что тоже самое буду сам управлять порядком их вызова) то я не увижу никаких изменений в логе теста, если в исходном интерфейсе порядок функций будет каким-то образом изменён. Представь ситуацию с перегруженной функцией. Её добавили в конце существующего набора функций, в тестах вызвали в конце и в логе она появилась в конце. Тест работает, а интерфейс нет.

Второй вариант более правильный. Как мне кажется, небольшой его недостаток в том, что функции интерфейса всё-же вызываются. И если их порядок изменён, то это может приводить либо к проблемам в передаче параметров, либо к вызову функции по произвольному или нулевому адресу. Но в общем, это приемлемо для приёмочных тестов.
« Последнее редактирование: 20-11-2010 18:14 от Алексей1153++ » Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #29 : 20-11-2010 18:12 » 

Второй вариант лучше не по этому, а потому что он моделирует реальные условия.
судя по первому предложению ты до конца не понял идею.

Ты уверен в именах в своём посте?
Извините если что. Тяжело с мобильного писать.
Записан

Странно всё это....
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines