Как проверить, можно ли безопасно привести указатель void* к чему-то другому?
Допустим, у меня есть эта функция, которая является частью некоторого инструментария gui:
typedef struct _My_Struct My_Struct;
/* struct ... */
void paint_handler( void* data )
{
if ( IS_MY_STRUCT(data) ) /* <-- can I do something like this? */
{
My_Struct* str = (My_Struct*) data;
}
}
/* in main() */
My_Struct s;
signal_connect( SIGNAL_PAINT, &paint_handler, (void*) &s ); /* sent s as a void* */
Поскольку paint_handler также будет вызываться главным циклом GUI toolkit с другими аргументами, я не всегда могу быть уверен, что параметр, который я получаю, всегда будет указателем на s
.
Могу ли я сделать что-то вроде IS_MY_STRUCT
в функции paint_handler
, чтобы проверить, что параметр, который я получаю, можно безопасно вернуть в My_Struct*
?
6 ответов:
Ваш указатель
void
теряет всю информацию о типе, поэтому только этим вы не можете проверить, можно ли его безопасно привести. Программист должен знать, можно ли безопасно привестиvoid*
к типу.
К сожалению, нет функции для проверки того, что указатель был до того, как он появился в этом контексте (void).
Единственное решение, которое я могу придумать, это если вы поместите int _struct_id в качестве первого члена всех ваших структур. Этот id-член можно безопасно проверить независимо от типа, но это не сработает, если вы передадите указатели, которые не реализуют этот член (или int, char,... указатели).
Лучшее, что вы можете сделать, - это посмотреть, на что указывают данные, чтобы увидеть, есть ли у них признаки того, что вы хотите, хотя А) это не будет близко к гарантии и Б) может быть опасно, поскольку вы не знаете, насколько велика вещь, на которую указывают данные. Я полагаю, что это не более опасно, чем просто бросать его и использовать, но (как было предложено) было бы лучше изменить дизайн.
На самом деле нет в c.
void
указатели не имеют типа и должны быть приведены только тогда, когда вы действительно знаете, на что они указывают.Возможно, вместо этого вам следует пересмотреть свой дизайн; перепишите свой код так, чтобы не было необходимости в проверке. Это та же причина, по которой google запрещает RTTI в своем Руководстве по стилю .
Если вы создаете используемый тип, вы можете включить в него некоторую идентифицирующую информацию, которая поможет вам исключить некоторые пустые указатели как не относящиеся к типу, который вы ищете. Хотя у вас есть шанс, что какая-то случайная область памяти будет содержать те же данные или сигнатуру, что и то, что вы ищете, по крайней мере, вы будете знать, когда что-то не было тем типом, который вы искали.
Этот подход потребует, чтобы структура была инициализируется таким образом, что члены сигнатуры, используемые для определения недопустимости области памяти, инициализируются значением сигнатуры.
Пример:
Это своего рода грубый подход, однако он может помочь. Естественно, использование макроса типаtypedef struct { ULONG ulSignature1; // .. data elements that you want to have ULONG ulSignature2; } MySignedStruct; #define MYSIGNEDSTRUCT_01 0x1F2E3D4C #define MYSIGNEDSTRUCT_02 0xF1E2D3C4 #define IS_MY_STRUCT(sAdr) ( (((MySignedStruct *)sAdr)->ulSignature1 == MYSIGNEDSTRUCT_01 ) && (((MySignedStruct *)sAdr)->ulSignature1 == MYSIGNEDSTRUCT_02))
IS_MY_STRUCT()
, где аргумент используется дважды, может быть проблематичным, если аргумент имеет побочный эффект, поэтому вам придется быть осторожным с чем-то вродеIS_MY_STRUCT(xStruct++)
, гдеxStruct
является указателем наMySignedStruct
.