LINUX.ORG.RU
ГолосованияГолосования (не подтверждено)

Какой кусок исходного кода на Си Вам кажется более красивым (см. подробности)

 , ,


0

2

первый вариант:

a[0] = get_data(0);
a[1] = get_data(1);
a[2] = get_data(2);
a[3] = get_data_3();
a[4] = get_data(4);
a[5] = get_data_5();

второй вариант:

for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
  switch(i) {
    case 3: a[i] = get_data_3(); break;
    case 5: a[i] = get_data_5(); break;
    default: a[i] = get_data(i); 
  }
}
★★★★★

Последнее исправление: Virtuos86 (всего исправлений: 2)

Где вариант «пишите свой код сами»?

toyo-chi
()

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

Такой код обычно бывает в двух случаях. Либо во всех этих get_data_* скрыты тривиальные формулы, и тогда вообще вопрос — стоит ли их оформлять как отдельные функции. Либо тут наоборот, скрыт айсберг сложности, который в C++ обычно разруливают через классы и виртуальные методы (в Си, о котором речь, такое тоже можно навелосипедить руками, если что).

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

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

Посмотрел на обе эти штуки, Эскобар. Абстракции ради абстракции

Первое решается struct, второе union в отношении применимости к Си. Реализация в С (что бы прям похоже было) будет состоять из макросов чуть более чем полностью и то с поправочками. Был бы в этом смысл практический, я б ещё помучался. А так.

Раз уж такие пироги можно посмотреть на пример где это реализовано красиво? И ещё до кучи можно посмотреть случаи когда такие вещи необходимы и без них никак в том плане что они лучший вариант? Я без тролинга. Просто интересно. Я в них смысла не вижу как в чём то отдельном.

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

третьим вариантом допишите

Те два варианта говно, этот правильный. Но поздно ты спохватился, опрос уже зафейлен.

no-such-file ★★★★★
()

Для конкретно этого случая - конечно первый. Второй - лютый оверинжиниринг

rk-d
()
Ответ на: комментарий от monk

a[2] = get_data(8); // заменили порт

Тогда можно a[2] = get_data_8() по логике кода.

Или если добавится

Аналогично.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от hotpil

Зачем может понадобиться выделять get_data_3 отдельно, почему нельзя сделать get_data(3)?

Т.е. предлагаешь сделать switch, но спрятать его внутрь get_data? Мало ли зачем нужно брать данные из другого источника.

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

Если уж делать то явно типа такого

#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>


typedef struct
{   bool  have;
    char * t;
}optional_string;

typedef struct
{   bool  have;
    int   t;
}optional_int;

typedef struct
{   bool  have;
    bool  t;
}optional_bool;

extern optional_string  get_name(int id)
{
    if(id == 5)
    {
        return (optional_string){true,"Bob"};
    }
    if(id == 6)
    {
        return (optional_string){true,"Alisa"};
    }

    return (optional_string){.have=false};
}
extern optional_int     get_age (int id)
{
    if(id == 5)
    {
        return (optional_int){true,36};
    }
    if(id == 6)
    {
        return (optional_int){true,22};
    }

    return (optional_int){.have=false};
}
extern optional_bool    get_banana(int id)
{
    if(id == 5)
    {
        return (optional_bool){true,true};
    }
    if(id == 6)
    {
        return (optional_bool){true,false};
    }

    return (optional_bool){.have=false};
}

typedef struct 
{
    optional_string name;
    optional_int    age;
    optional_bool   banana;
}user;


user get_user(int id)
{
    user u =
    {
        get_name  (id),
        get_age   (id),
        get_banana(id),
    };

    return u;
}

void print_user(user u)
{
    (u.age.have)?printf("age=%d ",u.age.t):0;
    (u.name.have)?printf("name=%s ",u.name.t):0;
    (u.banana.have)?printf("banana=%d \n",u.banana.t):0;
}



typedef struct
{
    uint8_t is_string:1;
    uint8_t is_int   :1;
    uint8_t is_float :1;
    uint8_t is_user  :1;

    union
    {
        char * t_char;
        int    t_int;
        float  t_float;
        user   t_user;
    };

}variant;


variant variant_set_int(int val)
{
    variant v = {0,0,0,0,0};
    v.is_int = 1;
    v.t_int  = val;
    return v;
};
variant variant_set_float(float val)
{
    variant v = {0,0,0,0,0};
    v.is_float = 1;
    v.t_float  = val;
    return v;
};
variant variant_set_char(char * val)
{
    variant v = {0,0,0,0,0};
    v.is_string = 1;
    v.t_char   = val;
    return v;
};
variant variant_set_user(user val)
{
    variant v = {0,0,0,0,0};
    v.is_user = 1;
    v.t_user  = val;
    return v;
};


#define  variants(val) \
         _Generic( (val),\
         int:   variant_set_int,\
         float: variant_set_float,\
         char*: variant_set_char,\
         user:  variant_set_user)(val);\



void print_variant(variant v)
{
    (v.is_string)?printf(">> %s\n",v.t_char):0;
    (v.is_int)   ?printf(">> %i\n",v.t_int):0;
    (v.is_float) ?printf(">> %f\n",v.t_float):0;
    (v.is_user)  ?print_user(v.t_user):0;
}


int main(int argc, char *argv[])
{

    user alisa  = get_user(5);
    user bob    = get_user(6);
    user bill   = get_user(7);

    print_user(alisa);
    print_user(bob);
    print_user(bill);


    variant v1 = variants(bob.name.t);
    variant v2 = variants(alisa.age.t);
    variant v3 = variants(bill.name.t);
    variant v4 = variants(bob.age.t);


   print_variant(v1); 
   print_variant(v2); 
   print_variant(v3);
   print_variant(v4); 
    return 0;
}
LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

optional_string optional_int

Вы так ничего и не поняли. В нормальном языке я лёгким движением руки пишу:

fn read<T: FromData>(&mut self) -> Option<T> {
    let v = self.data.get(0..T::SIZE).and_then(T::parse);
    self.advance(T::SIZE);
    v
}

И мне не нужно писать тонные неподдерживаемого ужаса.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

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

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

До тех пор пока типов 2.5

Нуууу, это да. Согласен. Но тут уже ничего не попишешь. Что есть то есть.

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

Ну, на вкус и цвет.

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

Мне нравятся примитивные языковые конструкции оперируя которыми я выражаю свои мысли. Мои конструкции статичны и имеют одно описанное ими поведение.

И то и то чаще всего фломастеры и вкусовщина. И то и то имеют право на жизнь. Тут нет лучше или хуже. Тут просто это есть и всё.

Понятое дело что удобно разными данными манипулировать однотипно используя более высокоуровневые конструкции языка которые позволяют манипулировать разными данными в одном как бы сказать хз контексте что ли в зависимости от этих данных. Но это не значит что это лучше по определению и точка. Где-то лучше. А где-то из пушки по воробьям.

Я вот пишу маленькие простые программы. Мне нужна однозначность то есть прямое описание того что я делаю. И тут это лучший вариант.

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

Где тут лучше? Нигде.

И да на равне с тобой как ты фыркаешь от примитивности базовых возможностей С я фыркаю от инопланетности выражения базовых возможностей С++/Rust (Синтаксис). Так что что-же вкусовщина. Может твой пример красив с точки зрения заложенного в нем смысла и стоящих за ним механизмов, но вот с точки зрения крастоты синтаксической тут всё ооочень спорно и опять же вкусовщина.

И да _Generic() нахрена пропихнули не знаю, редко нужная какашка. Хотя, лень может победить и начну использовать чаще. Но надеюсь не буду )))))))))))))))))

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

Си не подходит для современного кода.

А если современный код для стиральной машинки с тремя кнопками и atmega8 внутри? :D Тоже не подходит?

LINUX-ORG-RU ★★★★★
()

auto get_data(int i) {
    switch(i) {
        case 3: return get_data_3();
        case 5: return get_data_5();
        default: return get_data(i); 
  }
}

for (auto i =0; i < sizeof(a)/sizeof(a[0]); i++) {
    data[i] = get_data(i);
}

Имхо как-то так.

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Мало ли зачем нужно брать данные из другого источника.

Если это данные из другого источника, то зачем их записывать в тот же массив?

В общем, смотри, ты можешь это воспринять или нет, мне собственно все равно. Но если в коде вылезают уродливые конструкции, это всегда значит, что-то спроектировано не так. Возможно, неудачно выбран формат хранения данных. Неправильно выделены какие-то функции (методы, процедуры) и т.п. Так как я не вижу весь код, да честно говоря и не хочу на него смотреть, то я не могу сказать, что конкретно там не так. Я думаю, ты в состоянии и сам проанализировать и сообразить, что нужно поменять, чтобы не возникало такого вот уродства.

hotpil ★★★★
()
Ответ на: комментарий от hotpil

это всегда значит, что-то спроектировано не так

С этим я согласен. Но это не означает, что что-то спроектировано не так в коде.

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

Напиши как надо

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

int get_data_proxy(int index) {
    int result;
    if (index == 3) {
        result = get_data_3();
    } else if (index == 5) {
        result = get_data_5();
    } else {
        result = get_data(index);
    } 
    return result;
} 

for (int i=0; i<5; i++]) {
    a[i] = get_data_proxy(i);
}
ya-betmen ★★★★★
()
#include <sys/types.h>

typedef /*define your data type here*/void *data_type;
typedef data_type (* data_init)(void);
static size_t const sa = 6;
static data_type a[sa];

static data_type get_data(size_t idx)
{
    data_type data = (data_type)0;
    /*your data init code should be here*/
    return data;
}

static inline data_type get_data_0(void)
{
    return get_data(0);
}

static inline data_type get_data_1(void)
{
    return get_data(1);
}

static inline data_type get_data_2(void)
{
    return get_data(2);
}

static data_type get_data_3(void)
{
    data_type data = (data_type)0;
    /*your data init code should be here*/
    return data;
}

static inline data_type get_data_4(void)
{
    return get_data(4);
}

static data_type get_data_5(void)
{
    data_type data = (data_type)0;
    /*your data init code should be here*/
    return data;
}

static data_init init[] = {
    get_data_0,
    get_data_1,
    get_data_2,
    get_data_3,
    get_data_4,
    get_data_5
};

#define CASSERT(name,cond) \
    typedef char name[2*!!(cond!=0)-1];

static void initdata(void)
{
    CASSERT(invalid_number_of_inits, sa == sizeof(init)/sizeof(*init));
    for (ssize_t i = sa; --i >= 0; )
    {
        a[i] = init[i]();
    }
}

:)

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

Если это данные из другого источника, то зачем их записывать в тот же массив?

Например, собираем мы данные с разных датчиков. Датчики разных производителей, поэтому разные get_data. А дальше эти данные как-то обрабатываются, поэтому данные собираются в массив. Теоретически можно вместо массива написать a1 = , a2 =, …, так как именно обход по массиву в обработке не ожидается (ожидается что-то вроде «сравнить a[1] и a[4], сравнить разницу между a[0] и a[1] и a[2] и a[3] и т.д.»), но это вроде ещё менее красиво.

monk ★★★★★
() автор топика

Очевидно, что в первом варианте легче прозевать ошибку, может даже найтись «доброжелатель», который перепишет «китайский код» на цикл с get_data(i) для всех индексов

annulen ★★★★★
()

Для 5 переменных одинаково приемлемы, хотя первый я бы порядок сменил чтобы иметь get_data сплошняком. Вцелом, с увличением количества переменных лучше становится второй вариант.

Slackware_user ★★★★★
()
Ответ на: комментарий от Slackware_user

Рекламные посты блюющих радугой наглядно показывают, что даже в пяти однообразно выглядящих строчках бывают ошибки

annulen ★★★★★
()

Про отрезать бейтсы тому васяну, что это написал, было?

tereshchenko ★★
()
Ответ на: комментарий от annulen

я просто чаще программирую в фортране, там в теории можно б было конструктор массива заюзать для такого.. не пробовал, н вроде реально сделать)

Slackware_user ★★★★★
()

Второй

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

Camel ★★★★★
()
Ответ на: комментарий от Slackware_user

В C тоже так можно, но читаемости в данном примере это не прибавит

annulen ★★★★★
()
Ответ на: комментарий от Satori

Чтобы последний подтверждённый провисел на главной и люди проголосовали, иначе просто потеряется.

Вот был нормальный интерсный опрос, например: Как часто вы используете терминал или эмулятор терминала?

А ты взял и слил его долбопрогом.

WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Ты ещё синтаксические ошибки поищи

pon4ik ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Второй, разумеется, но с такой длиной массива за него следует бить по рукам.

Не могу распарсить результат утверждения. С такой длиной массива какой вариант лучше?

monk ★★★★★
() автор топика
Ответ на: Второй от Camel

первый вариант наоборот гипнотизирует своей ритмичностью на экране монитора

А если бы было

a[0] = get_data(0);
a[1] = get_data(1);
a[2] = get_data(2);
a[3] = foo(); /////////////////
a[4] = get_data(4);
a[5] = bar(); /////////////////

, то было бы идеально?

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

Почему работает a_geti ???

Потому что неопределённое поведение.

utf8nowhere ★★★
()
Ответ на: комментарий от monk

Какой кусок исходного кода на Си Вам кажется более красивым

Второй, разумеется

Более красивый, при этом, не значит более правильный.

WitcherGeralt ★★
()
a[3] = get_data_3();
a[4] = get_data(4);

а нельзя вот эти get_data_3 и _5 спрятать внутрь get_data()? Потому как при такой чахарде индексов и и суффиксов можно долго втыкать в ошибку компиляции. Хотя сути это не поменяет и внутри get_data() то же самое начнётся со switch.

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

Тогда должны быть два разных массива. Зачем данные с разных датчиков в один массив засовывать? Массив предполагает однородность данных.

Допустим, этот массив используется дальше где-то. Как пользователь должен догадываться, что элемент a[3] пришел из другого места, чем элемент a[2]?

hotpil ★★★★
()
Ответ на: комментарий от hotpil

Зачем данные с разных датчиков в один массив засовывать? Массив предполагает однородность данных.

Так данные-то однородные. Всюду, например, температура. Просто 4 датчика одного типа, а по одному другого (другой формы, возможно другой технологии, и с другим протоколом передачи).

Как пользователь должен догадываться, что элемент a[3] пришел из другого места, чем элемент a[2]?

А там уже неважна модель. Важен номер (местоположение) датчика и его значение.

monk ★★★★★
() автор топика

Они так-то оба какие-то странные. Без контекста не понятно

tz4678 ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.