Возможно ли создать одну функцию вместо двух функций с одинаковой целью, но с разными типами аргументов? (Можно ли удалить повторяющийся код?)
Можно ли создать одну функцию вместо двух функций с той же целью, но с разными типами аргументов?
У меня есть две функции, написанные на C, они конвертируют изображение из RGB в HSV:
void png_rgb2hsv(pPNG_DATA data);
void jpg_rgb2hsv(pJPEG_DATA data);
они выполняют точно такую же вещь: они берут data- > row_pointers и циклируют его в течение данных цикла → height. Затем он преобразует значения, указанные данными → row_pointers. Вот как это работает. Но единственное отличие состоит в том, что структуры данных используют разные типы. Мне кажется бессмысленным использовать две функции для одной и той же вещи. Особенно, когда я добавляю больше функций для большего количества цветовых пространств.
Как эта проблема проектирования программ на практике решена на C?
Update:
Большинство читателей не поняли вопроса. Я не спрашивал о перегрузке. Это был вопрос о дизайне. Я спрашиваю, можно ли удалить избыточную функцию, "избыточный" код. Поскольку обе функции используют один и тот же код, делайте то же самое, но типы в аргументе функции различны, потому что один тип из libjpeg, а второй тип - из libpng. Я обнаружил, что это невозможно, потому что это означает использование одной переменной для двух разных типов.
Ответы
Ответ 1
Это невозможно в C, но во многих других языках программирования, таких как С++, это возможно.
Тем не менее, есть некоторые трюки вокруг этого ограничения в C, см. этот вопрос для получения дополнительной информации.
Одной из распространенных практик в C является размещение типа в имени функции, как в вашем примере.
Ответ 2
Использовать общий макрос:
void png_rgb2hsv(pPNG_DATA data);
void jpg_rgb2hsv(pJPEG_DATA data);
#define rgb2hsv(X) _Generic((X), pPNG_DATA: png_rgb2hsv, pJPEG_DATA: jpg_rgb2hsv)(X)
(Если ваш компилятор слишком стар и не поддерживает этот трюк (или вы не хотите его использовать по какой-либо причине), то похоже, что нет возможности иметь 2 функции с одинаковым именем и разными типами аргументов. Затем вам нужно будет выбрать другое решение.)
Обновление:
Я неправильно понял вопрос ОП.
Если вы хотите создать одну функцию для обоих типов, вы должны сделать что-то вроде этого:
void rgb2hsv_(Something *row_pointers, int height) // any member that you need goes here
{
/* your code */
}
#define rgb2hsv(obj) \
do { \
_Generic((obj), pPNG_DATA: pPNG_DATA, pJPEG_DATA: pJPEG_DATA) tmp = (obj); \
rgb2hsv_(tmp->row_pointers, tmp->height /*again, every member that you need should be stated here*/) \
} while (0)
Кроме того, если pJPEG_DATA
и pPNG_DATA
имеют абсолютно одинаковый внутренний макет (это означает, что их члены имеют один и тот же тип и указаны в одном порядке), вы можете попробовать следующее: (это не так безопасно как предыдущий, но по крайней мере он не похож на плохой хак)
void rgb2hsv(void *ptr)
{
pPNG_DATA data = (pPNG_DATA *) ptr; // it does not matter which of 2 types you use here
/* put your code that uses `data` here */
}
Но имейте в виду, что если вы поменяете 2 члена в любой из этих структур или каким-либо образом измените их внутреннюю структуру, это может перестать работать.
(Кроме того, вы должны знать: эти 2 метода - это просто сложные способы обхода, чтобы получить желаемый результат. Лучший способ - просто передать каждый необходимый член как отдельный аргумент и не выполнять этот макрос кунг-фу)
Ответ 3
Указатели Void совместимы с указателем на любой объект. Вы можете использовать void*
и добавить параметр "type".
void data2hsv(void *data, int datatype) {
if (datatype == 0) {
/* use png */
} else {
/* use jpg */
}
}
Изменить: ложь компилятору
void data2hsv(void *data) {
pPNG_DATA source = data; // if data is of pJPG_DATA type
// compiler will not catch the error
/* use source as if it was pPNG_DATA */
}
Ответ 4
В этом есть два разных способа. Вы создаете структуру, содержащую изображение, а также тег, что есть данные, что-то вроде этого:
struct img{
char *filetype;
void *data;
};
И затем вы создаете функцию, которая проверяет тип файла и вызывает любую функцию, которую вы хотите.
Или вы создаете структуру с указателями функций на все функции, которые вы хотите использовать, например:
struct imgfuns{
void (*rgb2hsv)(void *);
};
struct imgfuns *init_struct(void)
{
struct imgfuns *funs = malloc(sizeof(*funs));
if(funs == NULL)
return NULL;
if(png...)
funs->rgb2hsv = png_rgb2hsv;
else if(jpg...)
funs->rgb2hsv = jpg_rgb2hsv;
return funs;
}
int main(int argc, char *argv[])
{
struct imgfuns *funs;
funs = init_struct();
if(funs == NULL)
exit(1);
/* get data from somewhere */
funs->rgb2hsv(data);
free(funs);
return 0;
}
Но это может быть больно, если вам нужно использовать много разных функций, поскольку вам нужно отображать их для каждой функции. Но вы получите более чистый код, и вы можете обрабатывать все форматы в одном месте, а не создавать функции-обертки для каждого формата файла.
Дополнительную информацию можно найти здесь: Как работают указатели функций в C?
Ответ 5
В следующем порядке используется дополнительный параметр для информирования функции о том, какой тип данных изображения передается:
void rgb2hsv(void *data, int data_type)
{
char *row, *pix;
switch (data_type) {
case JPG: row= (pJPEG_DATA data)->row; break;
case PNG: row= (pPNG_DATA data)->row; break;
}
....
Ответ 6
Вы не можете перегружать функции на C, но вы можете организовать свой код, чтобы сделать его оптимальным. Я бы создал функцию для rgb2hsv
и получил бы тип параметра void *
, определял бы тип и использовал бы его, обрабатывая все возможные случаи. И вы должны использовать только эту функцию. На более низких уровнях вы все еще дублируете свою функцию, на более высоком уровне вам не придется вызывать две отдельные функции. Это упростило бы использование.