Помогите пожалуйста, уперся в стену, не обойти:
1. Выделяю память под структуру данных типа record, заполняю выделенный блок данными.
2. Указатель на данный блок памяти него вешаю в свойство Tag компонент формы.
3. Есть два типа записей. При обработке некого события, компонент по данному указателю должен получить данные.
Проблема в том, что компонент не в курсе, запись какого типа лежит по указателю. Есть ли какой то способ методами RTTI получить информацию о типе записи, размещенной по указателю?
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
18-04-2023 12:09 | Комментарий к предыдущим ответам
Ещё хотел добавить, что для 64-битного проекта tag тоже 64-битный. Видимо, для подобных целей.
14-03-2023 11:26 | Комментарий к предыдущим ответам
>>> Сейчас вспомнил приём, который используется в win api
Там не всё так просто. Там есть поле, которое содержит число "размер структуры". И вот по этому то размеру "знающие" определяют, есть ли в структуре новые поля. То есть тем самым гарантируется, что "старая" программа вызовет "новый" интерфейс и тот не поломает старую программу, а "новая" программа сможет вызвать "старый" интерфейс и тот просто выполнит ограниченный набор функций. Это не линукс, где чуть-чуть что-то поменялось - и ломается наглухо вся система.
>>> вытащить информацию о типе можно только "договорившись", что в первом байте
Там и так 32-разрядное число, куда надо впихнуть 64-разрядный указатель, а тут ещё предлагается "укоротить" указатель ещё на 8 бит, до 24 бит. Совести у вас нет :-)!
Еще посмотрите TValue из модуля RTTI. Можно создать переменную этого типа и передать ее адрес через Tag. В обработчике же ничего не надо знать про тип уже (TValue его хранит) просто с помощью метода
procedure ExtractRawData(ABuffer: Pointer);
можно извлечь значение. Конечно не видя как там у вас реализовано наверняка сказать нельзя, но возможно такой подход будет удобен, так как информацию о типе надо иметь только в том месте кода где вы заполняете Tag.
С помощью RTTI получить информацию о типе записи, размещенной по указателю нельзя никак. Но кто вам мешает передать информацию о типе, а не только адресс?
Передайте аддрес не на переременную, а на такую структу. Далее в тексте обработчика в зависимости от значения TypeInfo делаете приведение. В том классе который содержит обработчик там при создании класса создаете список типов, значения их инициализируете с помошью System.TypeInfo
Благодарю всех за внимание к моей проблеме!
Решил не мудрить, а разделить элементы на категории, каждой из которых присваивается свой тип записи. Таким образом я могу явно приводить нетипизированный указатель к нужному мне типу, поскольку заранее его знаю. Тем не менее, все ответы интересны для изучения и применения в будущем, когда текущий подход станет слишком сложен и не оправдан.
06-12-2022 02:48 | Комментарий к предыдущим ответам
А чем это отличается от записи с вариантами?
Принципиально ничем не отличается. Только запись с вариантами - более гибкое решение.
tag - в нашем случае нетипизированный указатель. Поэтому, наверное, вытащить информацию о типе можно только "договорившись", что в первом байте, например, закодирован тип.
А парикручивать сюда RTTI - намного более тяжелое решение, чем использование классов вместо структур.
>>> Думал, что как и для классов можно из экземпляра переменной вытянуть информацию о ее типе, даже если это не класс
Из переменной, может быть, и можно. Но вы же собираетесь сохранять указатель на запись в свойстве Tag, при этом информация о типе потеряется.
Кстати, экземпляр класса в Delphi знает о своем типе именно так, что в структуре, которой является тело экземпляра класса в начале идет сигнатура типа (насколько я помню, ссылка на VMT класса). Поэтому и предложил вам самодельный аналогичный механизм для записей.
Всем благодарю за внимание к моей проблеме и предложенные варианты ее решения! Насчет вариантных записей мысль интересная, как то забыл, поскольку ими в общем то крайне редко пользовался... По поводу классов, тоже выход. Но у меня по данной части программы в основном используются записи, я и решил их использовать. Думал, что как и для классов можно из экземпляра переменной вытянуть информацию о ее типе, даже если это не класс.
02-12-2022 14:24 | Комментарий к предыдущим ответам
>>> Т.е. тип один, но "знающий" юзает свои поля
А чем это отличается от записи с вариантами?
Использовать классы... если бы типов структур было много, и их многообразие постоянно увеличивалось бы, тогда было бы оправдано.
Но когда типов структур всего два... использовать классы, чтобы Дельфя сама прочитала первые 4 байта и определила по ним тип... можно, наверное, но я и сам могу их прочитать :-)
01-12-2022 20:53 | Комментарий к предыдущим ответам
Не мучайтесь. Перейдите на классы. Базовый только идентификатором типа, а все остальные его потомки со своими любыми полями. Вначале читаете указатель приведением к базовому классу, а потом исходя из идентификатора типа - приводите к нужному классу.
Генерите нужный класс, присваиваете ему нужный тип. Все.
Увеличение расхода памяти в %-ном отношении мизерное.
Скорость доступа... Да, ниже. Но Вам же не ядерным реактором управлять - наносекунды роли не играют.
30-11-2022 22:29 | Комментарий к предыдущим ответам
Сейчас вспомнил приём, который используется в win api: функции принимают параметром указатель на структуру,
но есть новые, более продвинутые, которые "знают", что в этой структуре есть новые поля, которые расположены в конце.
Т.е. тип один, но "знающий" юзает свои поля. Но это, скорее, подойдёт, если большинство полей у структур одинаковые.
30-11-2022 13:41 | Комментарий к предыдущим ответам
Мне кажется, приведение указателя к integer и хранение в Tag - обычное дело. Скорее всего следовало бы к cardinal, но tag имеет тип integer. Однако, какая разница, нужные 32 бита присвоятся. В новый средах tag имеет "плавающий" тип, видимо для совместимости с 64 битными приложениями.
GEO описал решение, но если структуры создаются и освобождаются динамически, то можно без лишних заморочек заменить их экземплярами классов. Тогда с определением типа проблем не будет.
Я не самый большой спец по использованию RTTI, но в данном случае что-то вообще невероятное. Поскольку свойство Tag имеет тип Integer, то чтобы присвоить ему указатель на запись, вы должны будете выполнить приведение типов, как-то так:
MyComponent.Tag := Integer(@MyRecord);
Откуда вообще компонент узнает про тип записи, если вы говорите ему что это целое число? Даже если вы сможете в run-time определить тип MyRecord, всё равно надо будет как-то эту информацию сообщить компоненту.
Вариантов напрашивается два:
1. Кроме указателя на данные сохранять в компоненте ещё и тип записи (зачем вообще гемор с тэгом? выведите себе потомка из требуемого компонента, добавив поле/свойство для указателя на запись, для типа записи да хоть подо что).
2. Расширьте ваши записи, добавив в каждую из них первым полем тип записи. Тогда запись сама будет содержать информацию о своем типе.
А можно вообще использовать запись с вариантами, тогда у вас останется record одного единственного типа, содержащего две структуры:
type
TRecordType = (rtFirstType, rtSecondType);
TRecordData = record
case RecordType: TRecordType of
rtFirstType: (
Field1_1: Word;
Field1_2: ShortString;
Field1_3: array[0..9] of Byte);
rtSecondType: (
Field2_1, Field2_2: Integer;
Field2_3: TDateTime);
end;
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.