LINUX.ORG.RU
Ответ на: комментарий от dataman

May not work on 32-bit systems.

Просто потрясающе. И да, там нет _Generic.


#define __print_typeid(a) \
	__builtin_choose_expr(__print_is_type(a, double), 1, \
	__builtin_choose_expr(__print_is_type(a, float), 1, \
	__builtin_choose_expr(__print_is_type(a, char), 2, \
	__builtin_choose_expr(__print_is_type(a, unsigned char), 3, \
	__builtin_choose_expr(__print_is_type(a, int), 4, \
	__builtin_choose_expr(__print_is_type(a, unsigned int), 5, \
	__builtin_choose_expr(__print_is_type(a, long), 6, \
	__builtin_choose_expr(__print_is_type(a, unsigned long), 7, \
	__builtin_choose_expr(__print_is_type(a, short), 4, \
	__builtin_choose_expr(__print_is_type(a, char*), 8, \
	__builtin_choose_expr(__print_is_type(a, char[]), 9, \
	__builtin_choose_expr(__print_is_type(a, void*), 10, \
	__builtin_choose_expr(__print_is_type(a, int[]), 11, \
	__builtin_choose_expr(__print_is_type(a, unsigned int[]), 12, \
	__builtin_choose_expr(__print_is_type(a, short[]), 13, \
	__builtin_choose_expr(__print_is_type(a, unsigned short[]), 14, \
	__builtin_choose_expr(__print_is_type(a, char*[]), 15, \
	__builtin_choose_expr(sizeof(a) == 1, 2, \
	__builtin_choose_expr(sizeof(a) == 2, 4, \
	(0)  )))))))))))))))))))
hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)

Использовали в рабочем проекте:

/*! \brief Подписывает пользователя на требуемое событие
 * \param[in] name Имя события
 * \param[in] func Функция обратного вызова для этого события
 * \return Истина в случае успеха */
#define hrn_subscribe(name, func)                           \
    _hrn_subscribe(                                         \
        name,                                               \
        _Generic((func),                                    \
            hrn_callback_int        : HRN_CALLBACK_INT,     \
            hrn_callback_string     : HRN_CALLBACK_STRING,  \
            hrn_callback_json       : HRN_CALLBACK_JSON,    \
            hrn_callback_binary     : HRN_CALLBACK_BINARY,  \
            default                 : HRN_CALLBACK_NONE     \
        ),                                                  \
        func                                                \
    )

Без _Generic надо было бы передавать еще и тип.

PPP328 ★★★★★
()
Последнее исправление: PPP328 (всего исправлений: 1)
Ответ на: комментарий от cumvillain
#define FMT_IMPLEMENT
#define FMT_SHORTS
#include "c11/fmt.h"

int main(void) {
    const double pi = 3.141592653589793;
    const size_t x = 1234567890;
    const char* string = "Hello world";
    const wchar_t* wstr = L"The whole";
    const char z = 'z';
    _Bool flag = 1;
    unsigned char r = 123, g = 214, b = 90, w = 110;
    char buffer[64];

    print("Color: ({} {} {}), {}\n", r, g, b, flag);
    println("Wide: {}, {}", wstr, L"wide world");
    println("{:10} {:10} {:10.2f}", 42ull, 43, pi);
    println("{:>10} {:>10} {:>10}", z, z, w);
    printd(stdout, "{:10} {:10} {:10}\n", "Hello", "Mad", "World");
    printd(stderr, "100%: {:<20} {:.*} {}\n", string, 4, pi, x);
    printd(buffer, "Precision: {} {:.10} {}", string, pi, x);
    fmt_println("{}", buffer);
    fmt_println("Vector: ({}, {}, {})", 3.2, 3.3, pi);

    fmt_stream ss[1] = {0};
    printd(ss, "{} {}", "Pi is:", pi);
    print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap);
    printd(ss, "{} {}", ", Pi squared is:", pi*pi);
    print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap);
    fmt_close(ss);

    time_t now = time(NULL);
    struct tm t1 = *localtime(&now), t2 = t1;
    t2.tm_year += 2;
    fmt_print("Dates:\n  {}\n  {}\n", fmt_tm("%Y-%m-%d %X %Z", &t1),
                                      fmt_tm("%Y-%m-%d %X %Z", &t2));
}
dataman ★★★★★
()

Особо ненужно, но ты сам отетил на свой вопрос, для создания обобщённого интерфейса для разных типов, дабы не к void * всё приводить и по мере надобности всё приводить в нужному типу.

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

Удобно для тех вещей что в кишках и наружу не торчат там можно всё обособленно сделать код с обособленной работе с типами.

Короче применения можно найти, но как по мне все они сводятся к частным случаям которые порой надёжнее и без генериков сделать, а порой лучше с генериками, но лучше где то поглубже не наружу ИМХО.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

Я делал отладочную печать. Что-то вроде DEBUG_PRINT(str, i64, i32, i16). И оно в зависимости от типа каждого аргумента вызывает нужную функцию. Без такой фичи пришлось бы писать что-то вроде DEBUG_PRINT_STR(str); DEBUG_PRINT_I64(i64); ...

По сути перегрузка функций вручную.

vbr ★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Просто для чего-то сложнее sqrt обычно требуются своя структура с данными и что-то похожее на это:

struct foo {
    void (*do_shit)(struct foo *f);
};

static struct foo_one {
    struct foo;
    int a;
};

static do_shit(struct foo *x)
{
    struct foo_one *f = to_foo_one(x);
    printf("%d\n", f->a);
}

void keklol(struct foo *f)
{
    f->do_shit(f);
} 

Просто потому что скорее всего там дохренище сильно различается логика и в итоге все равно проще сделать через классы.

cumvillain
() автор топика
Ответ на: комментарий от cumvillain

Просто потому что скорее всего там дохренище сильно различается логика

И чё =), Си это тот язык где типами ты рулишь все они в твоей власти. Создавай тип любой, вешай на него любую логику, генериком объединяй обработку разных типов с одним ожидаемым результатом. Например тебе в программе нужно получать пиксель из картинки, одна картинка в GPU и её надо извлечь сначала, вторая уже в памяти лежит и просто надо взять пиксель, третья вообще в файле и нужно сначала загрузить и всё прочее сделать следующая например вообще по сети из интернетов, но излишне это прописывать и так понятно.

#include <stdio.h>

typedef struct
{
   int id; //GL_UINT
}texture;

typedef struct
{
   char * data;
   size_t size;
   int   depth;
   int   mode;
}image;

typedef struct
{
    char * path;
}file;


void get_pixel_texture(texture tex)
{
    printf("get pixel from texture\n");
}

void get_pixel_image(image img)
{
    printf("get pixel from image\n");
}

void get_pixel_file(file fle)
{
    printf("get pixel from file\n");
}

#define  get_pixel(object) \
         _Generic( (object),\
         texture: get_pixel_texture,\
         image: get_pixel_image,\
         file: get_pixel_file)(object);

int main(int argc, char *argv[])
{
    file    f;
    image   i;
    texture t;

    get_pixel(f);
    get_pixel(i);
    get_pixel(t);
    return 0;
}
dron@gnu:~$ gcc gen.c && ./a.out 
get pixel from file
get pixel from image
get pixel from texture
dron@gnu:~$

Вот и всё. Плюс если ты в get_pixel сунешь по ошибке не тот тип, то программа просто не соберётся

gen.c:38:20: error: ‘_Generic’ selector of type ‘struct bad_struct’ is not compatible with any association
   38 |          _Generic( (object),\
      |                    ^
gen.c:55:5: note: in expansion of macro ‘get_pixel’
   55 |     get_pixel(b);
      |     ^~~~~~~~~

Так что зря ты. Возможно ты подумал что генерик разрешает только фундаментальные типы char, int и подобные

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 4)
Ответ на: комментарий от cumvillain

Вся беда генерика в синтаксисе, да не очень, может даже вот тебе неприятно, понять можно. У генерика ровно одна задача, дать возможность создавать один внешний интерфейс к обработке разных типов с автоматической передачей типа к нужному «исполнителю». Всё. Ни больше ни меньше. Точка. Тут больше и сказать нечего. =) Как ты это будешь использовать, ну тут уже как и всё в Си это твоя личная проблема =)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от cobold

Я не зря написал:

Кто-нибудь когда-нибудь смог найти этому применение кроме sqrt для разных типов?

Когда ты в условной libc делаешь матчинг для трех типов: да, ок. Когда ты пишешь код, который может быть модульным и может лежать в разных репозиториях, то _Generic оказывается напрочь бесполезен.

cumvillain
() автор топика
Ответ на: комментарий от cobold

В данном случае для наглядности тут и тут же можно генерик просто выкинуть так как нет проблемы написать нужную функцию явно. Но допустим я хочу написать библиотеку с векторной математикой там и там всякие vec2/vec3/mat4/quat и прочие матрицы с квартернионами, десяток другой типов будет. Что мне очень часто надо с ними делать? Правильно умножать,вычитать,делить,возводить в степень, это не числа просто это матрицы и всё такое, как очень большое удобство я могут для умножения сделать одну функцию mul и ей перемножить вектора {x,y,z} или 4x4 матрицы или умножить «вектор» на матрицу и так далее. Как в GLSL короче.

Оно же в compile time матчит тип аргумента?

Да

Я выше написал что всё зависит от частного случая. Вот когда он настаёт тогда и понятно мол ага, тут генерик был бы кстати. А так и без него жить можно, меня вот он прикалывает, но я его никогда не использую, ну потому что смысла нет. Вот когда приспичит тогда вот и. =)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от cumvillain

Я не зря написал:

Нет ты зря это написал причём второй раз так как тебе уже ответили на варианты применения.

_Generic оказывается напрочь бесполезен.

Потому что ты придумал случай когда он бесполезен. Не надо использовать вещи не по назначению (не ну можно, но в разумных пределах =) ), так как они всегда в таком случае будут бесполезны.

И без генериков жили и не тужили, что он есть что его нет, но как уже не раз писалось в частных случаях он оправдан и удобен. Смотри это как на обычный define который подкидывать типы нужным функция сам без твоих проверок, вот и всё, что от этого можно ещё хотеть?

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Расшифруй, я древний и нипанимат.

Любую дичь что есть в стандарте сишники любят нежной любовью. Тут недавно был топик про бесполезный кусок стандарта, который тоже нежно полюбили.

cumvillain
() автор топика
Ответ на: комментарий от cumvillain

Эммм, ещё раз перечитай что я писал тут сверху донизу =) В каком месте ты увидел любовь у меня к генерикам непонятно.

Отвечал я тебе в критическом плане потому что ты, ну давай в аналогию, захотел скосить поле газонокосилкой, да у тебя это будет плохо получаться, ты будешь ругаться что это херня какая то и будешь совершенно прав для той цели которую ты выбрал газонокосилка просто не подходит. А когда тебе говорят что мол вот я ей газон ровняю, а я кое как на огороде осоку высокую выстригаю. Ты в ответ говоришь, ну блин фиг с вашими газонами и огородами, ваша газонокосилка на поле показывает себя как днище, мы тебе отвечаем ДА, ну блин оно не для этого хоть ты лоб себе расшиби. А ты продолжаешь, ну так значит газонокосилка же как сущность фигня ненужная если на поле себя не показывает как надо.

И всё, для твоих хотелок генерик просто не работает, просто по определению, он не плохой и не хороший. Он такой какой есть и ещё раз повторю, нужен он редко и в частных случаях. Это просто очередной сахарок для подсовывания данных нужному обработчику и всё. Тут любить или нет просто нечего. Так что не выдумывай тут на меня пожалуйста.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

О чем и речь. Была возможность вставить в язык хотя бы ограниченные дженерики, но опять обосрались и сделали нишевую убогость, которая никому не нужна кроме 1% от 1%.

cumvillain
() автор топика
Ответ на: комментарий от cumvillain

Если речь про Си, то виртуальные методы (да и вообще указатели на функции) надо использовать только там, где без них никак не обойтись. Потому как передача адреса кода через переменную повышает поверхность атаки. Именно через такие конструкции (хотя, не только через них) экспойты повреждения памяти превращаются в RCE.

Так что, возможность условного полиморфизма без использования самодельной динамической линковки кода - вполне полезная вещь. Хотя я ни разу этой конструкцией ещё не пользовался.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Если речь про Си, то виртуальные методы (да и вообще указатели на функции) надо использовать только там, где без них никак не обойтись. Потому как передача адреса кода через переменную повышает поверхность атаки. Именно через такие конструкции (хотя, не только через них) экспойты повреждения памяти превращаются в RCE.

Так написан почти весь софт сложнее курла. Хотя там тоже вроде то же самое, потому что бекенды.

cumvillain
() автор топика
Ответ на: комментарий от cumvillain

1) слишком категоричное и обобщающее заявление;

2) что значит «сложнее» - не до конца понятно, курл весьма сложная штука;

3) обычно такой софт пишут не на Си а на С++ или чего ещё похуже, и да часто оно дырявое, впрочем причин тому много кроме этой

4) разумеется, на каком-то уровне этого не избежать, но лучше ограничивать применение и не использовать везде просто так

firkax ★★★★★
()