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");

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


Делать множество фактически дублирующихся функций?

будь мужиком, напиши свой препроцессор ;)

stevejobs ★★★★☆
()
struct {
  union {
    struct netlist n;
    struct subcircuit s;
    struct somethingelse e;
  } u;
  enum {
    NetList, SubCircuit, SomethingElse
  } tag;
} tagged;

...

switch(s->tag){
  case NetList: ...
  case SubCircuit: ...
  ...
}

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

switch - это внутри функции? Хотелось бы всё-таки, чтобы на вход можно было подать любой объект, они фактически одинаковые ведь у меня для неё. Т.е. как-то так

struct comment *
add_comment(struct object *o, int lineno, char *text)
{ ...

mosfet
() автор топика

Еще можно вынести все общие поля в отдельную структуру, у которой один из элементов будет либо union либо void pointer на структуру с различающимися полями.

rand
()

ну вообще-то в сишке так не принято, вроде.
А реализовать можно кучей способов.
например сделать первым полем структуры int(информация о типе) и принимать void* или другую структуру, содержащую первым полем int и потом приводить типы.
кроме того когда ты вызываешь функции у тебя тип уже известен, т.к. тебе нужно решать в первое или во впторое место поставить.
Т.е. можно сделать 2 разные функции для разных типов. И так как код мало отличается чтобы не дублировать можно сгенерить макросом. но это лучший коммент: Написание функции, принимающей структуры разного типа (комментарий)

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

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

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

Пока что хочу обойтись Pure C, Flex и Bison. Позже планировал прикрутить сверху Perl. Всё это найдётся на любой целевой машине. И потом, это больше для обучения, ну и Си вспомнить :)

mosfet
() автор топика

Для каждого типа объявите разные функции, а в кишки им вставьте один и тот же макрос.

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

ну вообще-то в сишке так не принято, вроде.

да, скриптовые языки развращают

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

А, ну да, стоя и в гамаке - это как раз для обучения самое то.

anonymous
()
struct common
{
	struct comments;
	struct ncomments;
	struct lastcomments;
};

struct netlist {
	struct common;
	//everything else;
};

struct subcircuit{
	struct common;
	//everything else;
}

void add_comment(struct common *, const char *) {
	//impl here;
}

int main()
{
	struct netlist *nl = (struct netlist *) malloc(sizeof(struct netlist));
	struct subcircuit *sc = (struct subcircuit *) malloc(sizeof(struct subcircuit));
	add_comment((struct common *)nl, "abc");
	add_comment((struct common *)sc, "abc");
}

c++ с тепмлейтами смотрит на это уныло.

nanoolinux ★★★★
()
$ cat main.c
#include <stdio.h>


struct base {
    int field1;
    const char * field2;
};

struct AAA {
    struct base base;
    int field3;
};

struct BBB {
    struct base base;
    char * field3;
};


void dump_base(struct base* obj) {
    printf("struct base %p {\n", obj);
    printf("    field1 = %d;\n", obj->field1);
    printf("    field2 = '%s';\n", obj->field2);
    printf("}\n");
}

void dump_AAA(struct AAA* obj) {
    printf("struct AAA %p {\n", obj);
    dump_base( (struct base *)obj );
    printf("    field3 = %d;\n", obj->field3);
    printf("}\n");
}

void dump_BBB(struct BBB* obj) {
    printf("struct BBB %p {\n", obj);
    dump_base( (struct base *)obj );
    printf("    field3 = '%s';\n", obj->field3);
    printf("}\n");
}


void foo(struct base* obj) {
    if(obj) {
        obj->field1 = 123;
        obj->field2 = "hello from `foo`";
    }
}


int main() {
    struct AAA a_obj;
    struct BBB b_obj;

    a_obj.base.field1 = 1;
    a_obj.base.field2 = "base of AAA";
    a_obj.field3 = 100500;

    b_obj.base.field1 = 2;
    b_obj.base.field2 = "base of BBB";
    b_obj.field3 = "qwerty";

    printf("-- AAA BEFORE foo\n");
    dump_AAA(&a_obj);
    foo( (struct base *)&a_obj );
    printf("\n-- AAA AFTER foo\n");
    dump_AAA(&a_obj);

    printf("\n-- BBB BEFORE foo\n");
    dump_BBB(&b_obj);
    foo( (struct base *)&b_obj );
    printf("\n-- BBB AFTER foo\n");
    dump_BBB(&b_obj);

    return 0;
}
anonymous
()

Только хардкор!

#include <stdio.h>
#include <assert.h>

enum {TYPE_A, TYPE_B};

struct common {
	int type;
	int a;
	int b;
};

struct specific_a {
	struct common common;
	int z;
};

struct specific_b {
	struct common common;
	int x;
	int y;
};

#define DEF_A(x) struct specific_a (x) = {.common.type = TYPE_A}
#define DEF_B(x) struct specific_b (x) = {.common.type = TYPE_B}
#define COMMON(x) ((struct common*)(x))

void print(struct common *common) {
	printf("type: %d\n", common->type);
	printf("a: %d,  b: %d\n", common->a, common->b);

	switch (common->type) {
		case TYPE_A: {
				struct specific_a *a = (struct specific_a*)common;
				printf("z: %d\n", a->z);
			}
			break;
		case TYPE_B: {
				struct specific_b *b = (struct specific_b*)common;
				printf("x: %d, y: %d\n", b->x, b->y);
			}
			break;
		default:
			assert(0);
	}
}

void f(struct common *common) {
	common->a = 10;
	common->b = 20;

	switch (common->type) {
		case TYPE_A: {
				struct specific_a *a = (struct specific_a*)common;
				a->z = 30;
		 	}
			break;
		case TYPE_B: {
				struct specific_b *b = (struct specific_b*)common;
				b->x = 40;
				b->y = 50;
			}
			break;
		default:
			assert(0);
	}

}

int main(int argc, char **argv) {
	DEF_A(sa);
	DEF_B(sb);

	f(COMMON(&sa));
	f(COMMON(&sb));

	printf("sa:\n");
	print(COMMON(&sa));

	printf("sb:\n");
	print(COMMON(&sb));

	return 0;
}
AptGet ★★★
()

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

для тебя C++ придумали. И для таких задач. Pure C не для этого.

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

И потом, это больше для обучения

Обучения тому, как на не предназначенном для этого языке навелосипедить убогий недополиморфизм?

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

Чтож все к этому слову привязались... Я уже почти набыдлокодил свой недопарсер на перле, но тут вдруг вспомнил про существование специально предназначенных для этого тулов. От Си мне всего-то нужно хранение выпарсенных данных, остальное хочу сделать более высокоуровневым.

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

Да, идеи с base/common хороши, остановлюсь на этом

mosfet
() автор топика

оберни выделение памяти под твои разнотиповые структуры в свой_с_балеринами_и_шампанским выделятор который вместе с обычным выделением отмечает в своём хранимом хэше соответствие адресс - тип.

затем.

для фунцкий которым пофиг на тип структуры .

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

ps. да структруры прибиты гвоздями и непермещаются .

qulinxao ★★☆
()

Почему бы просто не передавать указатели на эти три поля. Тред не читал.

anonymous
()
typedef enum {
DATA_TYPE_FOO,
DATA_TYPE_BAR,
...
} data_type_t;

typedef struct FOO {
....
} data_FOO_t

typedef struct BAR {
....
} data_BAR_t

do_smth(data_type_t type, void *data)
{
}
anonymous
()

Или так,

add_comment(&curr_netlist->comments, "abc");
add_comment(&curr_subcircuit->comments, "abc");

или так,

add_comment((struct comment *) curr_netlist, "abc");
add_comment((struct comment *) curr_subcircuit, "abc");

или так,

curr_netlist->add_comment (curr_netlist, "abc");
curr_subcircuit->add_comment (curr_subcircuit, "abc");

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

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

Ну вот насочинял такое, но оно выдаёт Segmentation fault. ЧЯДНТ?

#include <stdio.h>
#include <stdlib.h>


struct comment {
    int lineno;
    const char *text;
//    struct comment *next;
};


struct common_comments {
    int ncomments;
    struct comment *comments;
//    struct comment *last_comment;
};


struct netlist {
    int nsubcircuits;
//    struct subcircuit *subcircuits;
//    struct subcircuit *last_subcircuit;
    struct common_comments *comments;
};


struct subcircuit {
    const char *name;
    const char *ports;
//    int nelements;
//    struct element *elements;
//    struct element *last_element;
    struct common_comments *common_comments;
};


struct comment *
add_comment(struct common_comments **o, int lineno, const char *text)
{
    struct comment *c = (struct comment *) malloc(sizeof(struct comment));
    c->lineno = lineno;
    c->text = text;

    (*o)->ncomments++;
    (*o)->comments = c;

    return c;
}


int main(void)
{
    struct netlist *toplevel = (struct netlist *) malloc(sizeof(struct netlist));
    add_comment(&toplevel->comments, 11, "abc");

//    struct subcircuit *sub1 = (struct subcircuit *) malloc(sizeof(struct subcircuit));
//    struct subcircuit *sub2 = (struct subcircuit *) malloc(sizeof(struct subcircuit));
    return 0;
}
mosfet
() автор топика
Ответ на: комментарий от mosfet
struct common_comments {
    int ncomments;
//    struct comment *comments; // зачем лишняя индирекция?
    struct comment comments;
//    struct comment *last_comment;
};

...

struct comment *
add_comment(struct common_comments *o, int lineno, const char *text)
{
    struct comment *c = (struct comment *) malloc(sizeof(struct comment));
    c->lineno = lineno;
    c->text = text;

    o->ncomments++;
    o->comments = c;

    return c;
}

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

И названия неплохо бы подправить, а то »comments" аж три разных понятия покрывает.

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

Блин, сам запутался с этими твоими comments. Короче вставь общую структуру без индирекции в другие частные структуры, и передавай ее в add_comments(&частная->общая, ...).

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

Премного благодарен, разобрался.

Что в итоге, если кому ещё интересно:

#include <stdio.h>
#include <stdlib.h>


struct comment {
    int lineno;
    const char *text;
    struct comment *next;
};


struct common_comments {
    int ncomments;
    struct comment *comments_list;
    struct comment *last_comment;
};


struct netlist {
    int nsubcircuits;
//    struct subcircuit *subcircuits_list;
//    struct subcircuit *last_subcircuit;
    struct common_comments comments;
};


struct subcircuit {
    const char *name;
    const char *ports;
//    int nelements;
//    struct element *elements_list;
//    struct element *last_element;
    struct common_comments comments;
};


struct comment *
add_comment(struct common_comments *o, int lineno, const char *text)
{
    struct comment *c = (struct comment *) malloc(sizeof(struct comment));
    if(c == NULL)
        return NULL;

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

    if(o->comments_list == NULL)
        o->comments_list = o->last_comment = c;
    else {
        o->last_comment->next = c;
        o->last_comment = c;
    }
    o->ncomments++;

    return c;
}


//struct subcircuit *
//add_subcircuit(struct netlist *n, int lineno, const char *name)


int main(void)
{
    struct netlist *toplevel = (struct netlist *) malloc(sizeof(struct netlist));
    add_comment(&toplevel->comments, 11, "abc");
    add_comment(&toplevel->comments, 222, "defg");
    add_comment(&toplevel->comments, 3333, "hijkl");

    struct subcircuit *sub1 = (struct subcircuit *) malloc(sizeof(struct subcircuit));
    add_comment(&sub1->comments, 77, "qwerty");
    add_comment(&sub1->comments, 888, "asdfghjkl");

//    struct subcircuit *sub2 = (struct subcircuit *) malloc(sizeof(struct subcircuit));

    struct comment *c;

    printf("toplevel: %d\n", (&toplevel->comments)->ncomments);
    c = (&toplevel->comments)->comments_list;
    while(c != NULL) {
        printf("  line %d; text %s\n", c->lineno, c->text);
        c = c->next;
    }

    printf("sub1: %d\n", (&sub1->comments)->ncomments);
    c = (&sub1->comments)->comments_list;
    while(c != NULL) {
        printf("  line %d; text %s\n", c->lineno, c->text);
        c = c->next;
    }

    return 0;
}
toplevel: 3
  line 11; text abc
  line 222; text defg
  line 3333; text hijkl
sub1: 2
  line 77; text qwerty
  line 888; text asdfghjkl
mosfet
() автор топика
Ответ на: комментарий от mosfet

Если интересно — как это выглядит на плюсах:


#include <vector>
#include <cstdio>

// typedef std::pair<int, const char*> Comment;
struct Comment {
    int lineno;
    const char *text;
};

// typedef std::vector<Comment> CommentList;
struct CommentList {

    std::vector<Comment> comments;

    void print()
    {
        printf("comments = %lu\n", comments.size());
        for (Comment &c : comments)
            printf("  line %d; text %s\n", c.lineno, c.text);
    }
};

class NetList : public CommentList {

    int n_subcircuits;

  public:

    NetList(int n_subcircuits_, CommentList list_) :
        CommentList(list_), n_subcircuits(n_subcircuits_) {}

    void some_m() { /* ... */ }
};

class SubCircuit : public CommentList {

    const char *name, *ports;

  public:

    SubCircuit(const char *name_, const char *ports_, CommentList list_) :
        CommentList(list_), name(name_), ports(ports_) {}

    void some_m() { /* ... */ }
};

void some_f(NetList netlist, ...) { /* ... */ }

void some_f(SubCircuit subcircuit, ...) { /* ... */ }

int main()
{
    CommentList
        toplevel { { {11, "abc"}, {222, "defg"}, {3333, "hijkl"} } },
        sub1 { { {77, "qwerty"} } };

    sub1.comments.push_back({888, "asdfghjkl"});

    toplevel.print();
    sub1.print();

    NetList nl { 42, toplevel };
    SubCircuit sc { "...", "...", sub1 };

    nl.print();
    sc.print();

    nl.some_m();
    sc.some_m();

    some_f(nl);
    some_f(sc);
}
quasimoto ★★★★
()

А такой вариант вам не подойдет?

enum type
{
   type_netlist,
   type_subcircuit
};

struct comment* add_comment(type t, void* s)
{
   switch(t)
   {
   case type_netlist:
      // do somethig related to netlist
      break;
   case type_subcircuit:
      // do somethig related to subcircuit
      break;
   }
   return 0;
}

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

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

mosfet
() автор топика
Ответ на: комментарий от 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
()
Ответ на: комментарий от AlexCones

Для каждого типа объявите разные функции, а в кишки им вставьте один и тот же макрос.

Макросами можно функции объявлять.

Я делал так:

#define func(X,Y,Z) void myfunc ## X() { Y = Z; }
func(eeprom, a, 5); 
func(sram, a, 5);

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

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

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

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

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

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