Это правильное использование пустых указателей? - PullRequest
2 голосов
/ 14 апреля 2011

Этот вопрос касается целесообразности использования пустых указателей в конкретной реализации.

У меня есть относительно простая программа, которая состоит из бесконечного цикла. В каждом цикле программа перебирает фиксированный диапазон постоянных значений и вызывает функцию для каждого значения. Конкретная функция, которая вызывается, может быть одной из трех доступных и указана во время выполнения аргументом. Перед запуском бесконечного цикла существует блок условий, который устанавливает функциональный указатель на функцию на основе предоставленного аргумента. Таким образом, логика условий должна выполняться только один раз, а не на каждой итерации в каждом цикле.

Это я реализовал, и он работает хорошо, но я хочу сохранять состояние между каждым вызовом функции. Мое предложение состоит в том, чтобы сохранить состояние в структуре и передать эту структуру при вызове функции для каждого из значений. Проблема состоит в том, что каждой функции требуется отдельная структура для хранения различного набора значений ее состояния, а прототип всех трех функций должен быть совместимым (для указателя функции). Я намерен решить эту проблему, используя указатель void в прототипах трех функций, поддерживая, таким образом, совместимые прототипы, но позволяя передавать разные структуры каждой функции.

Вопрос в том; Является ли мое предложение подходящим использованием пустых указателей или оно вводит слишком много динамизма во время выполнения, и поэтому я должен переосмыслить свой подход?

Примечание. Невозможно использовать статические переменные в каждой из трех функций, поскольку структуры также должны быть доступны в бесконечном цикле, поскольку также требуется некоторая обработка до и после повторения диапазона значений.

Ответы [ 5 ]

5 голосов
/ 14 апреля 2011

До тех пор, пока вы соблюдаете правильный тип вызовов, это довольно простой и понятный способ выполнить то, что вы описываете.

2 голосов
/ 14 апреля 2011

Вы можете получить некоторую меру безопасности типов, используя union:

typedef struct {
        int a;
        char *b;
} s1;
typedef struct {
        double d;
        int *e;
} s2;
typedef union {
        s1 s1;
        s2 s2;
} ocd;
typedef int (*daemon_function)(ocd *);

Тогда все ваши функции могут иметь тип daemon_function, но принимать разные аргументы через ocd.s1 или ocd2.s2. Я бы назвал все это бесполезной занятой работой. Простой void* будет работать так же хорошо.

Вы также можете включить магическое число в начале своих структур, а затем функции могут проверить безопасность типов, посмотрев на магическое число и выяснив, верное ли оно:

#define MAGIC 0x4d475600L
typedef struct {
    long magic;
    /* ... */
} whatever;

А потом:

int f(void *p) {
    whatever *w = (whatever *)p;
    if(w->magic != MAGIC) {
        /* complain and go boom! */
    }
    /* ... */
}

Я делал трюк с магическими числами все время, когда у меня были дни программирования на Motif, вы пропускали множество void* указателей в разработке Motif / Xt / X11.

1 голос
/ 14 апреля 2011

Пустые указатели - это способ сообщить системе набора текста, что вы хотите, чтобы она перестала выполнять свою работу и доверяла, чтобы вы не ошиблись.Это правильное использование void *, единственная проблема в том, что вы потеряли доступ к любой проверке типов, которую выполняет ваш компилятор.Вы можете создать некоторые очень странные и трудно диагностируемые ошибки.Если вы уверены, что знаете, что делаете (вы говорите так же, как вы), и если вы проверили каждую строку кода несколько раз и уверены, что в нем нет логических ошибок, то все будет в порядке.

1 голос
/ 14 апреля 2011

void * довольно идиоматичен в C. Лично я использую его преимущественно, но всякий раз, когда я делаю это, я склонен использовать тегированные структуры для безопасности, то есть я помещаю уникальный идентификатор типа в начале каждой структуры, чтобы идентифицировать его.

0 голосов
/ 14 апреля 2011

Обычно все в порядке.Я действительно предпочитаю использовать контексты void *, но похоже, что вы хотите избежать этого.Поскольку у вас уже есть некоторый код, который анализирует аргумент и выбирает функцию, вы можете просто выбрать функцию в переключателе и явно вызывать ее для каждой итерации.

...