К сожалению, вам, как правило, приходится отказываться от этой безопасности при компиляции, если вы используете C. В лучшем случае вы можете получить предупреждение, но если у вас есть конструкция, которая равномерно набрасывает указатели функций таким образом, вы которые могут игнорировать или полностью отключить их. Вместо этого вы хотите сделать упор на достижение безопасных стандартов кодирования. То, что вы не можете гарантировать силой, вы можете сильно поощрять политику.
Я бы предложил, если вы можете себе это позволить, начать с использования аргументов и возвращаемых значений, а не целых указателей функций. Гибкое представление следующим образом:
typedef void* GenericFunction(int argc, void** args);
Это эмулирует возможность иметь VARIADIC обратных вызовов, и вы можете равномерно сделать проверку безопасности во время выполнения в отладочной версии, например, чтобы убедиться, что количество аргументов соответствует условиям:
void* MyCallback(int argc, void** args)
{
assert(argc == 2);
...
return 0;
}
Если вам нужно больше безопасности, чем это для отдельных аргументов, передаваемых и можете позволить себе типично небольшую стоимость дополнительного указателя на каждом аргумент с немного громоздким структурированным решением, вы можете сделать что-то вроде этого:
struct Variant
{
void* ptr;
const char* type_name;
};
struct Variant to_variant(void* ptr, const char* type_name)
{
struct Variant new_var;
new_var.ptr = ptr;
new_var.type_name = type_name;
return new_var;
}
void* from_variant(struct Variant* var, const char* type_name)
{
assert(strcmp(var->type_name, type_name) == 0 && "Type mismatch!");
return var->ptr;
}
void* pop_variant(struct Variant** args, const char* type_name)
{
struct Variant* var = *args;
assert(var->ptr && "Trying to pop off the end of the argument stack!");
assert(strcmp(var->type_name, type_name) == 0 && "Type mismatch!");
++*args;
return var->ptr;
}
С макросами, как так:
#define TO_VARIANT(val, type) to_variant(&val, #type);
#define FROM_VARIANT(var, type) *(type*)from_variant(&var, #type);
#define POP_VARIANT(args, type) *(type*)pop_variant(&args, #type);
typedef struct Variant* GenericFunction(struct Variant* args);
Пример обратного вызова:
struct Variant* MyCallback(struct Variant* args)
{
// `args` is null-terminated.
int arg1 = POP_VARIANT(args, int);
float arg2 = POP_VARIANT(args, float);
...
return 0;
}
Дополнительным преимуществом является то, что вы можете увидеть в вашем отладчике при трассировке в MyCallback
через эти type_name
полей.
Это может быть полезно, если ваша кодовая база поддерживает обратные вызовы в динамически типизированные языки сценариев, поскольку языки сценариев не должны делать типы приведения в свой код (как правило, они должны быть немного на более безопасной стороне) , Имена типов затем могут использоваться для автоматического преобразования аргументов в собственные типы языка сценариев динамически с использованием полей type_name
.
вы можете написать '' my_dupe_fn'' с подписью typedef'ed "contract" и отбросить ваши аргументы внутри функции до требуемого типа. Тогда вам не понадобится бросок, и вы будете «надежны» в отношении этих потенциальных будущих изменений. Хорошим примером для этого является «DWORD ThreadProc (параметр void *)». Я никогда не видел, чтобы кто-то пытался отличить другую типизированную функцию к общей версии. Обычно люди вводят параметр в ожидаемый тип внутри '' ThreadProc''. – BitTickler
Есть несколько способов, чтобы кто-то не делал что-то глупое в C, и это не одно из немногих. Это точка зрения * cast *, чтобы принудительно сказать компилятору «Я нахожусь на этом, не беспокойся на вашей стороне». Любой указатель кода может быть вынужден носить эту маскировку. В конечном счете, это * обязанность звонящего * обеспечить правильный интерфейс. Если они заставляют бросить, * они * сбросили мяч. Они должны писать правильную функцию соответствия и * не * отличать их fptr * точно *, чтобы разрешить эту ситуацию при изменении интерфейса. – WhozCraig
Не уверен, но: действительно ли приведение 'my_dupe_fn' действительно требуется?void * может быть назначен любому другому типу указателя без приведения в действие, но я не уверен, работает ли это с этой косвенностью. – Olaf