Какие методы / стратегии люди используют для создания объектов в C (не в C ++)? - PullRequest
7 голосов
/ 04 августа 2009

Меня особенно интересуют объекты, предназначенные для использования изнутри C, а не реализации объектов, которые составляют ядро ​​интерпретируемых языков, таких как python.

Ответы [ 6 ]

9 голосов
/ 04 августа 2009

Я склонен делать что-то вроде этого:

struct foo_ops {
    void (*blah)(struct foo *, ...);
    void (*plugh)(struct foo *, ...);
};
struct foo {
    struct foo_ops *ops;
    /* data fields for foo go here */
};

С этими определениями структуры код, реализующий foo, выглядит примерно так:

static void plugh(struct foo *, ...) { ... }
static void blah(struct foo *, ...) { ... }

static struct foo_ops foo_ops = { blah, plugh };

struct foo *new_foo(...) {
   struct foo *foop = malloc(sizeof(*foop));
   foop->ops = &foo_ops;
   /* fill in rest of *foop */
   return foop;
} 

Затем в коде, который использует foo:

struct foo *foop = new_foo(...);
foop->ops->blah(foop, ...);
foop->ops->plugh(foop, ...);

Этот код можно привести в порядок с помощью макросов или встроенных функций, чтобы он выглядел более похожим на C

foo_blah(foop, ...);
foo_plugh(foop, ...);

хотя, если вы используете достаточно короткое имя для поля "ops", простое написание кода, показанного изначально, не особенно многословно.

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

7 голосов
/ 04 августа 2009

Вы используете термин «объекты» немного расплывчато, поэтому я предполагаю, что вы спрашиваете, как использовать C для достижения определенных аспектов объектно-ориентированного программирования (не стесняйтесь поправлять меня в этом предположении. )

Метод Полиморфизм:

Метод полиморфизма обычно эмулируется в C с использованием указателей на функции. Например, если бы у меня была структура, которую я использовал для представления image_scaler (что-то, что берет изображение и изменяет его размеры к новым измерениям), я мог бы сделать что-то вроде этого:

struct image_scaler {
    //member variables
    int (*scale)(int, int, int*);
}

Тогда я мог бы сделать несколько скейлеров изображений как таковых:

struct image_scaler nn, bilinear;
nn->scale = &nearest_neighbor_scale;
bilinear->scale = &bilinear_scale;

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

Наследование

Наследование обычно достигается как таковое:

struct base{
   int x;
   int y;
} 

struct derived{
   struct base;
   int z;
}

Теперь я могу свободно использовать дополнительные поля производного, а также получать все «унаследованные» поля базы. Кроме того, если у вас есть функция, которая принимает только структуру базы. вы можете просто привести свой указатель на структуру в указатель на структуру без каких-либо последствий

4 голосов
/ 04 августа 2009

Библиотеки, такие как GObject .

В основном GObject предоставляет общий способ описания непрозрачных значений (целых чисел, строк) и объектов (путем ручного описания интерфейса - как структуры указателей на функции, в основном соотносимой с VTable в C ++) - можно найти больше информации о структуре в справочнике

Вы также часто выполняете вручную vtables, как в «COM в простом C»

3 голосов
/ 14 августа 2013

Как видно из просмотра всех ответов, есть библиотеки, указатели на функции, средства наследования, инкапсуляции и т. д., все доступны (C ++ изначально был интерфейсом для C).

Однако я обнаружил, что ОЧЕНЬ важный аспект программного обеспечения читаемость. Вы пытались прочитать код 10 лет назад? Как В результате я склонен придерживаться простейшего подхода при объекты в кл.

Задайте вопрос:

  1. Это для клиента с крайним сроком (если так, рассмотрите ООП)?
  2. Могу ли я использовать ООП (часто меньше кода, быстрее разрабатывать, более читабельно)?
  3. Можно ли использовать библиотеку (существующий код, существующие шаблоны)?
  4. Я ограничен памятью или процессором (например, Arduino)?
  5. Есть ли другая техническая причина для использования C?
  6. Могу я сделать мой C очень простым и читабельным?
  7. Какие функции ООП мне действительно нужны для моего проекта?

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

class_A.h:
  typedef struct _class_A {...} Class_A;
  Class_A* Class_A_new();
  void Class_A_empty();
  ...

#include "class_A.h"
Class_A* my_instance;
my_instance = Class_A_new();
my_instance->Class_A_empty();  // can override using function pointers
0 голосов
/ 04 августа 2009

Аналогично подходу Дейла, но немного больше - это то, как PostgreSQL представляет узлы дерева разбора, типы выражений и тому подобное внутри. По умолчанию используются структуры Node и Expr, аналогичные

typedef struct {
    NodeTag n;
} Node;

где NodeTag - это typedef для unsigned int, и есть заголовочный файл с набором констант, описывающих все возможные типы узлов. Сами узлы выглядят так:

typedef struct {
    NodeTag n = FOO_NODE;
    /* other members go here */
} FooNode;

и FooNode могут быть безнаказанно брошены на Node из-за причуды структур C: если две структуры имеют одинаковых первых членов, их можно привести друг к другу.

Да, это означает, что FooNode можно привести к BarNode, что вы, вероятно, не хотите делать. Если вам нужна правильная проверка типов во время выполнения, GObject - это то, что вам нужно, хотя будьте готовы ненавидеть жизнь, пока вы ее изучаете.

(примечание: примеры из памяти, я давно не взламывал внутренние компоненты Postgres. FAQ для разработчиков *1019* содержит больше информации.)

0 голосов
/ 04 августа 2009

Посмотрите на реализацию IJG . Они не только используют setjmp / longjmp для обработки исключений, у них есть vtables и все. Это хорошо написанная и достаточно маленькая библиотека, чтобы вы могли получить очень хороший пример.

...