Контекст
( Актуальный вопрос ниже )
Легко в Delphi
Я регулярно конвертирую заголовки API в Delphi.Иногда возникают проблемы с выравниванием структуры, или я пропускаю опцию упаковки, например #pragma pack(push, 1)
.Чтобы проверить правильность выравнивания и размера, я отображаю смещения в своих переведенных записях с помощью этой простой функции, используя расширенный RTTI:
procedure WriteOffsets(Info: Pointer);
var
LContext: TRttiContext;
LType: TRttiType;
LField: TRttiField;
begin
LType := LContext.GetType(Info);
if Assigned(LType) then
if LType.TypeKind = tkRecord then
begin
Writeln((' ' + LType.QualifiedName + ' = record ').PadRight(48),
' // size ', LType.TypeSize);
for LField in LType.GetFields do
Writeln((' ' + LField.Name + ': ' + LField.FieldType.Name + ';').PadRight(48),
' // offset ', LField.Offset);
Writeln(' end;');
Writeln;
end
else
begin
Writeln(LType.QualifiedName, ' is not a record');
end
else
Writeln('Invalid type');
end;
, а затем вызываю его с помощью:
WriteOffsets(TypeInfo(TSecurityDescriptor));
Это хорошо записывает размер и смещения для каждого поля-члена, поэтому я могу проверить это с помощью аналогичного вывода, сгенерированного C ++ Builder.Функция генерирует что-то вроде этого:
Winapi.Windows._SECURITY_DESCRIPTOR = record // size 20
Revision: Byte; // offset 0
Sbz1: Byte; // offset 1
Control: Word; // offset 2
Owner: Pointer; // offset 4
Group: Pointer; // offset 8
Sacl: PACL; // offset 12
Dacl: PACL; // offset 16
end;
Не так просто в C ++ Builder
Но в C ++ Builder это не так просто.В настоящее время я использую несколько макросов и простой RTTI (typeid(x).name()
), например:
members.h
#ifndef MEMBERS_H
#define MEMBERS_H
void print_member(const char *type, const char *name, int offset)
{
char buffer[256];
sprintf(buffer, " %s %s;", type, name);
printf("%-48s // offset %d\n", buffer, offset);
}
#define print(member) \
print_member(typeid(((type *)0)->member).name(), #member, offsetof(type, member))
#define start \
printf("struct %-42s/\/ size %d\n{\n", typeid(type).name(), sizeof(type))
#define end \
printf("};\n\n");
#endif
Я знаю, что это уродливо и делаетC ++ пуристы съеживаются, но это работает.Я использую его в простых тестовых программах, таких как:
#include "windows.h" // for the struct type I want to inspect
#include "members.h"
int main()
{
#define type SECURITY_DESCRIPTOR
start;
print(Revision);
print(Sbz1);
print(Control);
print(Owner);
print(Group);
print(Sacl);
print(Dacl);
end;
#undef type
}
И я получаю такой вывод:
struct _SECURITY_DESCRIPTOR // size 20
{
unsigned char Revision; // offset 0
unsigned char Sbz1; // offset 1
unsigned short Control; // offset 2
void * Owner; // offset 4
void * Group; // offset 8
_ACL * Sacl; // offset 12
_ACL * Dacl; // offset 16
};
Вопрос
Есть ли способ сделатьC ++ Builder часть более элегантной и производительной?В идеале я предоставляю только тип (например, SECURITY_DESCRIPTOR
), и код генерирует выходные данные для всех его элементов данных.
В настоящее время, в отличие от примера Delphi, я должен явно запрашивать каждое поле члена.Я получаю вывод, который мне нужен, но это гораздо больше работы, чем в Delphi.
Я не смог найти способ заставить C ++ Builder генерировать RTTI, аналогичный расширенному RTTI в Delphi.Есть ли способ, используя более расширенные возможности C ++ (или фактически C ++ Builder), например шаблоны и / или новые функции C ++ 11 (или C ++ 14?), Чтобы автоматизировать это лучше, то есть я простопредоставить имя структуры и код делает то же самое, что Delphi?Предоставляет ли C ++ Builder более качественный RTTI, чем базовый RTTI, который обеспечивает обычный C ++?
Примечание
Меня интересуют только данные простых членов (Windows 32 или 64 бит) Структуры POD API, а не в классах или структурах с (возможно, виртуальными) функциями-членами, которые используются в общем C ++.