LINUX.ORG.RU

Функции в структуре Си

 , ,


0

2
#include <stdio.h>

typedef struct object {
  int n;
  int (*prop)();
} object;

int main(void)
{
  int prop(int n)
  {
    return 64+n;
  };
  struct object obj = {26, &prop};
  printf("%d\n", obj.prop(obj.n));
  return 0;
}



В этом коде у меня есть некая структура object, в которой есть функция, а точнее указатель на функцию
В структуре также есть поле n, где хранится некое число
Я создаю структуру obj, куда заношу число и адрес функции в памяти
Говнокод ли это? Как сделать это лучше? Есть ли какой-нибудь аналог this из Java/JS/other-OOP-lang, что бы обратится к полю уже существующей структуры
Если в Си такой город а-ля ООП считается говном, то покажите пример, как сделать это лучше
Привычка организовывать пронстранства имен - плохо ли это в Си? Или забить и использовать static?
P.S.: Я только учусь, хочу стать Мастером Си, писать полезные приложения в 50 строчек и т.д. (шучу)

★★

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

Тебе нужен Си++.
В терминах Си ты что-то делаешь не так. У тебя при нормальной формулировке задачи для Си просто не должно быть желания пихать указатель на функцию в структуру. Это нелепо. Это как брюки с 5 штанинами. Возможно, но бессмысленно.

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

например, указатели на функции пихают в структуры в разных API, где нужно определить много коллбэков

libpurple к примеру

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

Вот и я о том, удобно, когда функции сгрупированны

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

У тебя вполне нормально все. Разве что для указателя на функцию я бы тип объявил.

И да, у тебя в структуре int (*prop)();, а в реале int prop(int n). Оно хоть и компиляется без вопросов, но может привести к проблемам (забудешь, что там 1 аргумент, да впихнешь где-нибудь два), потому и удобней typedef сначала сделать.

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

Не надо. GTK — такая дрянь, что просто жуть! Еще дрянности к нему добавляет использование идиотской glib.

Лучше какую-нибудь более приличную сишную библиотечку используй.

anonymous
()

Почему ты не читаешь K&R, но бежишь с этими вопросами сюда?

unt1tled ★★★★
()

Есть ли какой-нибудь аналог this из Java/JS/other-OOP-lang, что бы обратится к полю уже существующей структуры

Да, передавай объект первым параметров во все “методы”.

theNamelessOne ★★★★★
()

все нормально, только у тебя n случайно равен 0 ибо не инициализирован (зафигачь туда еще методы init/deinit или open/close для инициализации/деинициализации, аналоги конструкторов/деструкторов) и функция в main это явно расширение gcc и не переносимо

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

для ООП необязательно пихать указатели на функции в структуру. тем более, что это никак тебе не поможет получить this.

что тебе надо:

int prop (object *this) {
    return 64+this->n;
}

т.е. явная передача this.

а object определять как

typedef struct {
    int n;
} object;

waker ★★★★★
()

Привычка организовывать пронстранства имен - плохо ли это в Си? Или забить и использовать static?

в си пространство имен одно. делай префиксы в именах, типа mylib_prop, mylib_object.

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

А от большого числа функций stackoverflow не бывает?

стек никак не связан с количеством функций.

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

Покажи пример пожалуйста, а то я не очень то и понимаю )

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

Черт, а я дурак думал, что тут есть какие-то пространства имен, а оказывается все кучей в стеке :(

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

Указатели в структуре нужны для того, чтобы иметь возможность указать, скажем, конструктор или метод объекта. Скажем, один объект будет иметь метод «сумма», а другой — «разность».

А вот насчет this ты правильно говоришь: тяжко в С с этим. Можно, например, так выкрутиться:

#include <stdio.h>

typedef struct object {
  int n;
  int (*prop)(struct object *, int);
} object;

int main(void){
  int prop(object *self, int n){return n + self->n;}
  struct object obj = {26, &prop};
  printf("%d\n", obj.prop(&obj, 10));
  return 0;
}
Но это не помешает вместо self подсунуть другой obj.

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

Круто, я сделал printf(«%d\n», obj.prop(&obj)); и все :-)
Пока мне хватит и этого, еще раз спасибо
Прикольный язык Си, простенький

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

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

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

void object_init(object *this) {
    this->n = 0;
}

или, если не нужно уметь в объекты на стеке, то так:

object *object_init (void) {
    object *this = calloc (sizeof (object), 1);
    this->n = 0;
    return this;
}

зачем тут какие-то указатели на функции?

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

но в этом случае стоит задуматься, а нужен ли тебе Си.

waker ★★★★★
()

ООП на Си делается только так:

typedef struct {...} MyClass;
void MyClassMethod(MyClass *this);
При желании ещё можно воспользоваться opaque types, но никакие указатели на функции использовать не надо, только если нужно динамически менять реализацию.

Deleted
()

Если можешь избежать вложенных функций, избегай.

i-rinat ★★★★★
()

Кстати, если интересуют clang/gcc-специфичные расширения для ООП, то можно загуглить __attribute__((cleanup(...))) и про реализацию defer.

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

не только.

посмотри на gobject — там классы и объекты разные сущности, объекты ссылаются на классы, и содержат instance data, а классы ссылаются на методы.

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

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

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

зачем тут какие-то указатели на функции?

Ну вот пример: #include <stdio.h>

typedef struct object { int n; int (*prop)(struct object *, int); } object;

int main(void){ int plus(object *self, int n){return self->n + n;} int minus(object *self, int n){return self->n - n;} object objs[2] = {{26, &plus}, {99, &minus}}; printf(«1: %d, 2: %d\n», objs[0].prop(&objs[0], 10), objs[1].prop(&objs[1], 10)); return 0; }

gcc 2.c -o 11 && ./11 1: 36, 2: 89 Хотя у меня ни разу не возникало нужды в подобном — тащить вместе с объектом какие-то функции.

anonymous
()

Еще чуть чуть и ты изобретешь таблицу виртуальных методов :D

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

Хотя у меня ни разу не возникало нужды в подобном — тащить вместе с объектом какие-то функции.

правильно, потому что функции нужны _в классе_, а не в объекте.

а классы в примитивных объектных системах не используются.

классом, по сути, является набор методов для работы с объектом.

твой пример понятен, но это не самая лучшая реализация.

гораздо приятнее работать, если класс и объект разделены.

тогда у тебя получится вот такой класс:

typedef struct {
  void (*)prop(object *this, int n);
} class;

объект:

typedef struct {
  class *klass;
  int n;
} object;

создаем классы:

  class plus_class = {
    .prop = plus
  };
  class minus_class = {
    .prop = minus
  };

создаем объекты

  object objects[2] = {
    {
      .klass = &plus_class,
      .n = 10
    },
    {
      .klass = &minus_class,
      .n = 20
    }
  };

дальше дергаем:

objects[0].klass->prop(&objects[0], 50);
objects[1].klass->prop(&objects[1], 25);
waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 2)

Почему на ЛОРе столько примитивных вопросов по сишке? RTFM!

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

Плюсую. Учитывая, что именно в это разворачиваются методы в С++.

Только, лучше конечно, еще для собствннной безопансти поставить константу.

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

У тебя при нормальной формулировке задачи для Си просто не должно быть желания пихать указатель на функцию в структуру.

И кто же наградил вас таким великим знанием?

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

Именно поэтому я так заботливо вам его сообщаю.

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

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

Авторы SQLite, например, считают иначе, что у тебя есть им противопоставить?

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

С чего ты взял, что они так считают? Если они так делают, то не факт, что они считают это чем-то хорошим. Костыли, знаешь ли, призваны поддерживать код, а не просто уродовать фасад.

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

Если они так делают, то не факт, что они считают это чем-то хорошим.
Костыли, знаешь ли, призваны поддерживать код, а не просто уродовать фасад.

Возьмем публичное API:

int sqlite3_create_collation_v2(
  sqlite3*, 
  const char *zName, 
  int eTextRep, 
  void *pArg,
  int(*xCompare)(void*,int,const void*,int,const void*),
  void(*xDestroy)(void*)
);

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

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

а где тут пихание указателей на функции в структуру?

Здесь:

struct CollSeq {
  char *zName;          /* Name of the collating sequence, UTF-8 encoded */
  u8 enc;               /* Text encoding handled by xCmp() */
  void *pUser;          /* First argument to xCmp() */
  int (*xCmp)(void*,int, const void*, int, const void*);
  void (*xDel)(void*);  /* Destructor for pUser */
};
anonymous
()
Ответ на: комментарий от anonymous

Я не знаю что делает эта функция и поэтому мои возможности по исправлению всего этого невелики, но если ты считаешь, что int(*xCompare)(void*,int,const void*,int,const void*) не уродливо, то у меня для тебя плохие новости.
Впрочем, это, скорее всего, какой-то коллбек или что-то в этой роде.
А топик про попытку слепить что-то вроде плюсового класса, но в Си. И суть желания ТСа не в указателе на функцию как таковую, а в (цитирую) «организовывать пронстранства имен».

Stahl ★★☆
()

говнокод только в смысле что используется вложенная функция (main::prop) это gcc-изм, недопускаемый в классике С. Вынеси int prop() из main() и наступит счастье, для полноты можно ещё static.

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

но если ты считаешь, что int(*xCompare)(void*,int,const void*,int,const void*) не уродливо, то у меня для тебя плохие новости.

Оно уродливо только потому, что сам С уродлив. Или ты считаешь, что qsort, например, не из той же оперы?

А топик про попытку слепить что-то вроде плюсового класса, но в Си.

Если у тебя не было задач, где это напрашиваемое решение - это не повод вещать про свой единственно верный подход.

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

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

Ваш здравый смысл вас подводит.

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

И если нужны еще примеры, то, например, авторы питона тоже не гнушаются такого подхода.

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