Перегрузка аргументов функций в Cи

Перегрузка аргументов функций в C11 (в Си, не в Си++. С последним и так все понятно)

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

print_number( ... )

И вот хочется нам делать такие вот вызовы:

print_number(2.0f);
print_number(-12);
print_number(655350000000);
print_number("One");

Да еще чтобы компилятор не ругался и все работало. В Си++ мы просто пишем несколько функций с названием print_number, но с разными типами аргументов.

В Си, как и в Си++ нам понадобится написать несколько функций, реализующих эту печать, но есть проблема.
В Си нам нельзя иметь одинаковые названия функций, поэтому.. Пока дадим разные.

Напишем функции печати для каждого типа аргумента, а назовем их, например

print_number_float(float arg);
print_number_integer(signed int arg);
print_number_longlong(unsigned long long arg);

А нам хотелось бы , эх, чтобы у нас была ОДНА функция печати на все, ну уж точно не с разными именами.

И тут нам на помощь приходит малозаметная фишка GCC препроцессора, _Generic. Это часть стандарта C11. С помощю дженерика мы говорим препроцессору, на что заменить нашу писанину.

Вот наши печаталки:

void print_asciiz(const char *data);
void print_unsigned(unsigned int data);
void print_signed(signed int data);
void print_float(float data);

// .. и одна специальная, на всякий случай
void __attribute__((noreturn)) print_bug(unsigned int data) { abort(); }

А вот - правила замены: на что заменить слово “print_number(…)“ в зависимости от типа этого самого “…”


// Тип аргумента : На что менять
//
#define print_number( _Item ) _Generic((_Item), \
    const char *   : print_asciiz, \
    char *         : print_asciiz, \
    unsigned int   : print_unsigned, \
    unsigned short : print_unsigned, \
    unsigned long  : print_unsigned, \
    unsigned char  : print_unsigned, \
    signed int     : print_signed, \
    signed short   : print_signed, \
    signed long    : print_signed, \
    signed char    : print_signed, \
    float          : print_float, \
    double         : print_float, \
    default        : print_bug \
)( _Item)

Теперь в Си коде можно писать

print_number(2.0f);
print_number(-12);
//print_number(655350000000);   <--- заленился писать
print_number("One");

И компилятор каждый раз сгенерирует вызов той функции, которую вы ему указали в _Generic. Что-то вроде switch/case но на уровне
препроцессора.

Нескучного программирования :slight_smile:

4 лайка

Ахренеть! Не знал. Полезность сомнительна, но … ахренеть!

1 лайк

Я тут постил где-то маленькую рисовалку табличек. Ну, из плюсиков , палочек, там, тире. Ну и псевдографикой тоже. UTF8.

И там примерно такой сценарий использования

  table_layout_t t;

  // Задаем настройки: использовать символы UTF8 для рамок, печатать "% " (процент и пробел)
  // перед каждой новой строчкой и символ # с пробелом в конце каждой строчки
  table_layout(&t, false, "% ", " #\r\n");

  // Рисуем заголовок нашей таблички с названиями стобцов
  table_header(&t, "Column 1", "Colun 2","Col 3", "Colum Number Four", NULL);

  // Начинаем заполнять данными. Заполнение идет слева направо, сверху вниз.
  // Если значение не влазит по ширине, то будет обрезано
  table_data(&t, "Long Text");
  table_data(&t, "Short");
  table_data(&t, "Exact");
  table_data(&t, "Very Long Text");

  table_data(&t, 10000.123456f);
  table_data(&t, 655355555);
  table_data(&t, -65535);
  table_data(&t, "Another quite long text");

  table_data(&t, 1);
  table_data(&t, -1);
  table_data(&t, 666.666f);
  table_data(&t, 777.777);

Конечно, можно было бы не извращаться и написать все на Си++.

Но это не спортивно.

1 лайк

Вы пытаетесь холивар развести?

1 лайк

Нет, холисрач!

Да ладно к словам придираться. Нормально выпендрился!))

2 лайка