Префикс функции vs "Function Struct" в C - PullRequest
0 голосов
/ 20 сентября 2018

Я пытаюсь выяснить лучшие практики в C (11).Поскольку нет пространств имен, я могу придумать два подхода, чтобы избежать конфликтов имен:

a.) Префиксы функций

void kernel_init(void) { ... }
int kernel_start(void* foo) { ... }

b.) "Структуры функций"

struct kernel {
    void (*init)(void);
    int (*start)(void* foo);
} kernel;

Я не спрашиваю, какой подход красивее, потому что это довольно субъективно.Я спрашиваю: Существуют ли какие-либо заметные недостатки, подходящие к тому или иному подходу, кроме стиля кода? Это включает в себя небольшие вещи, которые поначалу не имеют отношения к делу, но становятся большими проблемами по мере роста базы кода.

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Чаще всего используется версия функции.

Недостатком версии struct является то, что она не является автономной.Указатели на функции должны быть установлены из «конструктора», а не из вызывающей стороны, так как это нарушило бы частную практику проектирования инкапсуляции.А что бы вы назвали своим конструктором?Вам понадобится префиксная функция независимо от того.

Структуры, подобные этой, часто просто используются, когда вы хотите достичь полиморфизма или когда у вас есть функции обратного вызова.Некоторые стили кодирования также используют структуры для эмуляции членов класса C ++, но вопрос о том, является ли это действительно хорошей практикой, спорен.

Лучшая практика ОО, скорее, требует третьей версии с использованием непрозрачных указателей, что означает, что вам нужны как функции, так и структуры:

typedef struct kernel_t kernel_t;

kernel_t* kernel_init (void) { ... }
int kernel_start (kernel_t* this, ...) { ... }

, где определение структуры видно только для "kernel.c", не для звонящего.

0 голосов
/ 20 сентября 2018

Интересно, я никогда не думал об этом решении раньше.

Первое, конечно, стандартное, и я готов поспорить, что это то, что вы найдете в подавляющем большинстве проектов C [*].

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

...
} kernel = {
  .init = kernel_init,
  .start = kernel_start,
};

Но, ооо, вы снова идете с префиксными функциями.Чтобы устранить необходимость в них, функции должны быть static, что, я думаю, возможно, если вы добавите extern к объявлению struct в kernel.h.

Так что более полный примерэто может быть:

// kernel.h (public header)
typedef struct kernel_api {
  void (*init)(void);
  int (*start)(void* foo);
} kernel_api;

extern const kernel_api kernel;

// in kernel.c
static void init(void)
{
  ...
}

static int start(void *foo)
{
  ..
}

const kernel_api kernel = {
  .init = init,
  .start = start,
};

Это может сработать, но я не пробовал.

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

[*] Я просто статистически утверждал, что видел (или думал) о подавляющем большинстве мировых проектов C, но это, конечно, неправда.:)

...