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