LINUX.ORG.RU

Написание функции, принимающей структуры разного типа

 


0

2

Пытаюсь сделать функцию, способную оперировать с различными структурами (на данный момент это struct netlist и struct subcircuit). Можно ли как-нибудь упростить этот код? Typedef тут поможет?

Пока объектов, которым можно сделать add_comment() всего два и достаточно if/else, но дальше ведь будет ещё хуже и кривее. Делать множество фактически дублирующихся функций?

struct comment *
add_comment(struct netlist *n, struct subcircuit *s, int lineno, char *text)
{
    struct comment *c = malloc(sizeof(struct comment));
    if(c == NULL)
        return NULL;

    c->lineno = lineno;
    c->text = text;
    c->next = NULL;

    if(n == NULL) {
        if(s->comments == NULL)
            s->comments = s->last_comment = c;
        else {
            s->last_comment->next = c;
            s->last_comment = c;
        }
        s->ncomments++;
    } else {
        if(n->comments == NULL)
            n->comments = n->last_comment = c;
        else {
            n->last_comment->next = c;
            n->last_comment = c;
        }
        n->ncomments++;
    }

    return c;
}

Естественно, struct netlist и struct subcircuit обе имеют поля comments, last_comment и ncomments.

Функцию вызываю так:

add_comment(curr_netlist, NULL, lineno, "abc");

add_comment(NULL, curr_subcircuit, lineno, "abc");

Си я последний раз видел в институте на первом курсе, если что.


Ответ на: комментарий от andreyu

Можно структуру с "magic" начинать:

typedef enum{
   m_1,
   m_2
} magic;

typedef struct{
   magic m;
   …
} struct1;

typedef struct{
   magic m;
   …
} struct2;

struct1 *init1(){
   struct1 *r = malloc(sizeof(struct1));
   r->m =  m_1;
   return r;
}

struct2 *init2(){
   struct1 *r = malloc(sizeof(struct2));
   r->m =  m_2;
   return r;
}

void fn(magic *s){
   if(*s == m_1){
      struct1 *s1 = (struct1*) s;
      …
   }else if(*s == m_2){
      struct2 *s2 = (struct2*) s;
      …
   }
…
}
}

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

А всё-таки приведение malloc к типу структуры ненужно? Часто встречаю такую запись

struct comment *c = (struct comment *) malloc(sizeof(struct comment));
хотя даже в старинной K&R написано, что этого делать не стоит.

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

Не обязательно, но желательно (если готовишь приличный код, а не быдлокод для себя).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от mosfet

А всё-таки приведение malloc к типу структуры ненужно?

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

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

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

Зато динамический полиморфизм во все поля. И я не думаю что парсинг от этого станет сильно медленнее. Ввод-вывод все равно гораздо тормознее.

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

ещё шажочек и будет OO на C :)

то есть почти-что glib ;) вместо вашего enum {} magick, заюзать указатель на почти vtable :)

/* a-la vtable entry */
struct Type {
  struct Type *ptype; /** parent type */
  int typeid;  /** id (or name) for serialize */
  /* constructor and destructor */
  void *(*construct)(struct Type *,void *buf,size_t bsize);
  void (*destruct)(struct Obj *);
  /* some methods */ 
  int (*method1)();
  int (*method2)();
};
/* common object header */
struct Obj {
  struct Type *type;  /** pointer to type descrptor */
  int nref;  /** nr of reference to object */
// int tag;  /** or tag for gard.collector */
};
/* sample "klass" */
struct struct1 {
  struct Obj self;
  int i,j,k; 
};
struct struct2 {
  struct Obj self;
  char *name;
};
/* call type method1 */
int fn(Obj *obj,int arg1,int arg2) {
  assert(obj!=NULL);
  assert(obj->type!=NULL);
  assert(obj->type->method1!=NULL);
  return obj->type->method1(arg1,arg2);
}

anonymous
()
Ответ на: комментарий от Eddy_Em

это не трындец, а «запах трупа страуса» :) ;из препроцессора строящего подобные конструкции вырос C++.

вполне правильный подход, учитывая что ТС на Сях ст кроит некий парсер для некоего языка. Всякая особо выделенная сущность (токен,лексема) реализуется единообразно. Может ему по ТЗ запрещён ++, но ОО подход это не отменяет.

к тому-же можно и продолжить использовать введёный вами в треде «magick», а упомянутый мной .self->type как «константу» - на протяжении времени исполнения он не меняется, так что конструкция «switch magick { case type_one: call_method_type1...case type_two:call_method_type2...} лишь слегка модифицируется. То есть это ортогональные решения.

„трындец“ есть и там и там.. :)

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

Да, можно в типы запихивать всякие конструкторы/деструкторы, но только если смысл в этом есть. А в конкретном случае такого смысла нет. Не надо усложнять.

А еще можно самым первым полем структуры объявить функцию-обработчик. Тогда вообще вместо `switch` будет вызываться self->proc(self).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Я так понимаю, в этом треде решили изобрести что-то вроде Objective C? :) ТЗ я себе сам определяю, тут ограничений нет, я просто делаю как можно проще и как умею. Для этой задачи ради 5-7 функций ООП городить, думаю, не стоит, оно у меня будет уровнем выше.

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

А еще можно самым первым полем структуры объявить функцию-обработчик. Тогда вообще вместо `switch` будет вызываться self->proc(self).

можно. если автору нужна только одна функция. И это гораздо лучше чем создание enum { type_1..type_2}.

Образно - enum это константа. Необоснованое создание константы не имеющей образа/значения в реальном мире - зло. Это прямой способ прибить код к текущему моменту и впоследствии выбросить его на помойку. Жалко потом потраченного времени.

Да, код с энумами работает быстрее, но живёт меньше. Проц. быстрее стправляется с константами чем со всякими косвенными указателями и прочими таблицами.

Но программы пишутся в первую очередь для людей - помилуйте тех кто будет сопровождать - как им помнить что если например magick:=:MYGREATYPE то второй параметр slowfunc должен быть NULL ??

кстати отчасти этим (magick-constant) отчасти отличаются программы и библиотеки сделанные community just-for-fun и по заказу. В первом случае выходит реюзабельный годами код, бо авторам жаль своего личного времени и сил, и все хотят получить нечто что пригодтся в другом; а во втором «сдал-получил-забыл» - главное деньги, сдесь и сейчас :)

anonymous
()
Ответ на: комментарий от mosfet

в старинной K&R написано, что этого делать не стоит.

Правильно написано. Семантически это корректно, а религию каждый выбирает себе сам.

anonymous
()
Ответ на: комментарий от mosfet

какие бывают ещё программы и библиотеки?

ещё бывают программы партии и ленинские библиотеки. Хотя судя по постановке вопроса, последнее не про вас

anonymous
()
Ответ на: комментарий от Eddy_Em

Можно структуру с «magic» начинать:

Можно. Пусть ТС сам решает, как ему удобнее.

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