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)

Всю тему читать не стал, видел вариант с Designated Inits. Предлагаю чуть более простой, хотя и GCCизм:

int (*getters[N])(int) = {[0 ... N-1] = get_data};
getters[3] = get_data_3;
getters[5] = get_data_5;

for (int i = 0; i < N; i++) a[i] = getters[i](i);

Сигнатуры функций можно подогнать, чтоб было однородненько.

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

Предлагаю чуть более простой, хотя и GCCизм:

Если без него, тогда

int (*getters[N])(int) = {};
getters[3] = get_data_3;
getters[5] = get_data_5;

for (int i = 0; i < N; i++) a[i] = (getters[i] || get_data)(i);

Вроде должно работать.

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

тогда вопрос решается через функцию-обертку

typedef enum { SENSOR1, SENSOR2, SENSOR3, SENSOR4 } Sensors;

float temperature(Sensors sensor) {
    float value = 36.6;

    switch (sensor) {
    case SENSOR1:
        value = get_data_1();
        break;
    case SENSOR2:
        value = get_data_2();
        break;
    case SENSOR3:
    case SENSOR4:
        value = get_data(sensor);
        break;

    default:
        // BAD
        // should never reach this point
        assert(0);
    }
    return value;
}

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

тогда вопрос решается через функцию-обертку

Если имеет смысл делать функцию, которая запускается ровно один раз. А без функции это вариация второго варианта.

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

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

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

если за время жизни программы мы снимаем показания датчиков ровно один раз - достаточно простынки текста + комментарии

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

Нет, так не работает. Можно либо так:

int (*getters[N])(int) = {};
getters[3] = get_data_3;
getters[5] = get_data_5;

for (int i = 0; i < N; i++) a[i] = (getters[i] ? getters[i] : get_data)(i);

но это несколько более неуклюже, либо так:

int (*getters[N])(int) = {};
getters[3] = get_data_3;
getters[5] = get_data_5;

for (int i = 0; i < N; i++) a[i] = (getters[i] ?: get_data)(i);

но это опять GCCизм

Barracuda72 ★★
()

case 3:
case 5:

Магические константы в case? Но зачем?

Xintrea ★★★★★
()

Данный тред - отличная демонстрация того, что демократия не работает. Большинство выбрали более простой и не правильный вариант.

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

Если элементов константно 5 и после получения данных их надо отправлять дальше вместе то это единственно верный вариант будет =) Всё зависит от условий. Если условия меняются то я уже отписал вариант для этого

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

Скорее наглядная демонстрация того, что мнение большинства отнюдь не всегда совпадает с мнением большинства активно высказывающихся.

Варианты оба правильные (работают ведь). А кому какой приятнее было бы встретить в незнакомом проекте, который надо отредактировать, — вопрос вкуса.

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

но это опять GCCизм

Вообще, если подумать, то вызов get_data_3(void) как get_data_3(i) тоже в некотором смысле GCCизм. Ведь на любом компиляторе с stdcall (например, Watcom) такой вызов сразу испортит стек.

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

Всё же первый вариант для таких случаев понятнее. Но enum/define для констант не помешал бы (что за особые случаи такие), либо в док рядом референс на объяснение кинуть.

Result-Code
()

Намаппать по указателю структуру уже советовали?

nihirash ★★★
()

Не стоит увеличивать сложность на ровном месте. Она и сама прекрасно растёт.

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

Только я бы ещё static_assert на размер массива поставил, чтобы пришлось поменять инициализацию при изменении размера.

А если уж делать второй вариант, то тогда нужно делать отдельную функцию get_data_wrapper, в которой в зависимости от i будет вызываться правильный вариант, и свитч запихивать уже в неё.

Ivan_qrt ★★★★★
()

Притча о двух стульях какая-то… Я за первый вариант, потому что он проще визуально воспринимается. Но оба варианта выглядят отвратительно.

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

Я об этом вроде упомянул, что надо привести все функции к одному интерфейсу. На всякий пожарный.

Кстати, автор не привел деклараций функций. Если там get_data_3(), т.е. «любое число любых аргументов», то при stdcall стек может и не попортиться.

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

the result shall compare equal to the original pointer

Ну и где UB, дядя? Там речь про то, что функции могут быть в разных моделях памяти. Во flat всё пучком. Ну и про вызов там вообще ничего нет.

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

Если там get_data_3(), т.е. «любое число любых аргументов»

Не надо рассказывать сказки. Пустые скобки это не «любое число любых аргументов».

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

Я даже осилил их понять

То, что ты не нашёл слов про неопределённое поведение, явно говорит о том, что не осилил даже прочитать.

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

Не надо рассказывать сказки. Пустые скобки это не «любое число любых аргументов».

N1570, 6.7.6.3 Function declarators (including prototypes), п. 14:

«… An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied

Информация о параметрах не указана => их может быть сколько угодно и каких угодно.

Сложно спорить со стандартом C.

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

whose type is not compatible with the referenced type

Тебе уже предыдущий оратор повозил рожей, что это не not compartible тип. Мне лень тебе разжёвывать смысл того, что ты пытаешься цитировать как мартышка. Слова он там увидел, ЛОЛ.

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

Информация о параметрах не указана => их может быть сколько угодно и каких угодно.

🤦‍♂️

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

Фактически же вызывать функцию с параметрами, несовместимыми с тем, как определена функция — UB.

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

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

это не not compartible тип

Приведи определение compatible типов.

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

Стадарт: If a converted pointer is used to call a function…

no-such-file: Ну и про вызов там вообще ничего нет.

🤦‍♂️

ты не осилил прочитать

Я даже осилил их понять.

Давно так не смеялся.

Ну а вообще смеяться грех. Такая самоуверенность + невнимательность похожа на гипоманию.

utf8nowhere ★★★
()
Последнее исправление: utf8nowhere (всего исправлений: 1)

Первый вариант сходу понятен. Второй курить надо. Я предпочитаю читать первый.

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

Второй вариант если есть несколько частных случаев.

Но почему бы эти частные случаи не затолкать в функцию get_data(i), а внешний код оставить через цикл?

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

данные это код И код это данные

функции это объекты И объекты это функции

Структуры данных + Алго = Программы ( какой то из виртов с виртолётами из середины 90ых - дедушка вдарился в детские модельки на старости лет - как это было не современно в середине-конца 90ых )

? как когерентно мэнеджить набор значений

зы. в лишпах что данные что код всё шпишки.

ззы. в гарвардской не так как в манчестерской.

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

Данный тред - отличная демонстрация того. что демократия работает.

Большинство выбралО более простой и не правильный вариант.

зы. зы.

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

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

Но вариант рефакторинга скромно предложу: я бы сделал одну функцию-wrapper, внутри которой воткнул switch, где без цикла все эти условия разруливал. А сами значения в case я бы сделал константами. Эти ж цифры явно не с потолка взяты, а что-то значат. Мне этот вариант кажется наиболее «красивым», но он предложен автором темы не был, так что это притча о двух стульях, я считаю.

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

Но вариант рефакторинга скромно предложу: я бы сделал одну функцию-wrapper, внутри которой воткнул switch, где без цикла все эти условия разруливал.

Делать функцию, которая в коде вызывается только в одной точке? Получится второй вариант, но тело цикла будет в другом месте файла. То есть читать сложнее, исправлять также.

Эти ж цифры явно не с потолка взяты, а что-то значат.

Номер датчика = номер позиции на схеме размещения датчиков. Можно поименовать enum {SENSOR_1, … SENSOR_6}. Красивее?

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

Вот вам ООП :)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>

//
// Sensor interface
//
struct sensor_context_t;
struct sensor_io_t
{
    int  (*fn_get_sensor_data)(struct sensor_context_t*);
    void (*fn_free)(struct sensor_context_t*);
};

struct sensor_context_t
{
    const struct sensor_io_t* io;
};

int sensor_get_data(struct sensor_context_t *ctx)
{
    return ctx && ctx->io && ctx->io->fn_get_sensor_data ? ctx->io->fn_get_sensor_data(ctx) : -(ENOTSUP);
}

void* sensor_alloc(size_t size, const struct sensor_io_t* io)
{
    struct sensor_context_t* ctx = calloc(1, size);
    if (!ctx)
        return NULL;
    ctx->io = io;
    return ctx;
}

void sensor_free(struct sensor_context_t *ctx)
{
    if (ctx && ctx->io && ctx->io->fn_free)
        ctx->io->fn_free(ctx);
}



//
// Generic sensor (sample)
//
struct sensor_context_generic_t
{
    // must be first
    struct sensor_context_t parent;
    int index;
};

static int generic_sensor_get_data(struct sensor_context_t* ctx)
{
    struct sensor_context_generic_t* genctx = (struct sensor_context_generic_t*)ctx;
    return genctx->index;
}

// Dtor
static void generic_sensor_free(struct sensor_context_t* ctx)
{
    struct sensor_context_generic_t* genctx = (struct sensor_context_generic_t*)ctx;
    free(genctx);    
}

// Ctor
struct sensor_context_t* generic_sensor_create(int index)
{
    // Vtable ;-)
    static const struct sensor_io_t io = {
        .fn_get_sensor_data = generic_sensor_get_data,
        .fn_free            = generic_sensor_free,
    };
    
    struct sensor_context_generic_t *ctx = sensor_alloc(sizeof(struct sensor_context_generic_t), &io);

    // Sensor specific, I2C address, for example
    ctx->index = index;
    
    return &ctx->parent;
}


//
// Application code
//
enum {
    SENSOR_A,
    SENSOR_B,
    SENSOR_C,
    SENSOR_D,
    // last
    SENSOR_COUNT
};

static size_t n_sensors = 0;
static struct sensor_context_t** sensors = NULL;
void init_sensors()
{
    sensors = calloc(SENSOR_COUNT, sizeof(struct sensor_context_t*));
    if (!sensors)
        return;
    n_sensors = SENSOR_COUNT;
    
    sensors[SENSOR_A] = generic_sensor_create(31337);
    sensors[SENSOR_B] = generic_sensor_create(31338);
    // We must handle suche cases:
    //sensors[SENSOR_C] = generic_sensor_create(31339);
    sensors[SENSOR_D] = generic_sensor_create(31340);
}

void deinit_sensors()
{
    for (size_t i = 0; i < n_sensors; ++i) {
        sensor_free(sensors[i]);
    }
}

void ask_sensors()
{
    for (size_t i = 0; i < n_sensors; ++i) {
        const int value = sensor_get_data(sensors[i]);
        printf("Sensor %" PRIu64 " get %d\n", (uint64_t)i, value);
    }
}

int ask_sensor(int id)
{
	if (id < 0 || id >= n_sensors)
		return (-EINVAL);
	return sensor_get_data(sensors[id]);
}

int main()
{
    init_sensors();
    
    ask_sensors();
    
    // Single sensor
    {
    	int val = ask_sensor(SENSOR_B);
    	printf("\nSensor %d get %d\n", SENSOR_B, val);
    }
    
    // Wrong sensor
    {
    	int id = 31337;
    	int val = ask_sensor(id);
    	printf("\nSensor %d get %d\n", id, val);
    }
    
    deinit_sensors();
    return 0;
}

Погонять: https://ideone.com/EuFcaX

h4tr3d ★★★★★
()
Последнее исправление: h4tr3d (всего исправлений: 1)

Оба говно. А что, больше спрашивать не о чем?

deadplace
()

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

template <int k>                                                                
int get_data() {                                                                
  return k;                                                                     
}                                                                               
                                                                                
template <>                                                                     
int get_data<3>() {                                                             
  return 30;                                                                    
}                                                                               
                                                                                
template <>                                                                     
int get_data<5>() {                                                             
  return 50;                                                                    
}                                                                               
                                                                                
int main() {                                                                               
    int a[6];
                                                                   
    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>();                                                       
    
    return 0;                                                        
}                          

Конечно это сработает, только если все случае можно перебрать в compile-time

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

только если все случае можно перебрать в compile-time

Тогда уж больше шаблонов богу шаблонов:

#include <type_traits>

template <int k>                                                                
int get_data() {                                                                
  return k;                                                                     
}                                                                               
                                                                                
template <>                                                                     
int get_data<3>() {                                                             
  return 30;                                                                    
}                                                                               
                                                                                
template <>                                                                     
int get_data<5>() {                                                             
  return 50;                                                                    
}     

constexpr int size { 6 };

void get_all_data(int*, std::integral_constant<int, size>)
{
}
    
template<int index = 0>
void get_all_data(int *a, std::integral_constant<int, index> = std::integral_constant<int, 0>())
{
    a[index] = get_data<index>();
    get_all_data(a, std::integral_constant<int, index + 1>());
}

int main()
{
    int a[size];
    get_all_data(a);
    return 0;
}
monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Пиши на sheme и конпиляй через chicken-cheme в натив!

Ты тот результат компиляции видел? Он точно никому красивым не покажется.

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

Ты тот результат компиляции видел? Он точно никому красивым не покажется.

Видал =) Да, я так. Просто как вариант =)

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

Оба говно. Сделай кейс для 3 и 5 в get_data и убери это непотребство.

crutch_master ★★★★★
()

5 сраных значений инициализировать циклом?

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

movl $5, -36(%rbp)

только если исправить хардкод с i<6 на вычисление длины массива

как пишет это «нуб-monk» в с++

int main()
{
 int a[5];
 for (int i = 0, n = sizeof(a)/sizeof(a[0]); i < n; ++i)
 {
   a[i] = i + n;
 }
 return a[0];
}


как пишет это «мега-оптимизатор-halturin» в с++
int main()
{
 int a[5];
 for (int i = 0, n = 5; i < n; ++i)
 {
   a[i] = i + n;
 }
 return a[0];
}

И во что это превратить компилятор? :) (Нет я не забыл второй асм код. Они естественно идентичны)
	.file	"sizeof.cpp"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$48, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax
	movl	$0, -40(%rbp)
	movl	$5, -36(%rbp)
.L3:
	movl	-40(%rbp), %eax
	cmpl	-36(%rbp), %eax
	jge	.L2
	movl	-40(%rbp), %edx
	movl	-36(%rbp), %eax
	addl	%eax, %edx
	movl	-40(%rbp), %eax
	cltq
	movl	%edx, -32(%rbp,%rax,4)
	addl	$1, -40(%rbp)
	jmp	.L3
.L2:
	movl	-32(%rbp), %eax
	movq	-8(%rbp), %rcx
	xorq	%fs:40, %rcx
	je	.L5
	call	__stack_chk_fail@PLT
.L5:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	 1f - 0f
	.long	 4f - 1f
	.long	 5
0:
	.string	 "GNU"
1:
	.align 8
	.long	 0xc0000002
	.long	 3f - 2f
2:
	.long	 0x3
3:
	.align 8
4:

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

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

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

Фактически же вызывать функцию с параметрами, несовместимыми с тем, как определена функция — UB.

Функция может быть вообще никак не определена. Это может быть, например, ассемблерная процедура, принимающая первым параметром количество аргументов, каждым четным - тип аргумента, каждым нечетным - его значение. (Да, в современном C есть правильный механизм для этого, но в принципе такую конструкцию представить себе вполне реально).

Barracuda72 ★★
()

Самый корректный ответ тебе даст: bash «твой_компилятор» «ключ_сделать_асм» «ключи» «твой_файл.срр».

И сравни, что будет красивее смотреться....

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

но тело цикла будет в другом месте файла

И что? Зато будет единый метод get_data со switch внутри (get_data_3 и get_data_5 надо убрать, заменив все методом единым get_data). Это как раз не сложнее читать и не сложнее исправлять. Еще и юнит-тестом легче покрывается. Если в сях пишут юниты вообще (я не в теме).

dimuska139 ★★
()
Ответ на: movl $5, -36(%rbp) от LinuxDebian

Клоун?)) посмотри сначала исходное сообщение ТС, на которое я написал комментарий, а не отредактированное.

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