Меня особенно интересуют объекты, предназначенные для использования внутри C, в отличие от реализаций объектов, которые составляют ядро интерпретируемых языков, таких как python.Какие методы/стратегии используют люди для создания объектов в C (а не C++)?
ответ
Библиотеки, такие как GObject.
В основном GObject обеспечивает общий способ описания непрозрачных значений (целые числа, строки) и объекты (путем ручного описания интерфейса - как структуры указателей функций, в основном соответствующих VTable на C++). Более подробная информация о структуре может быть найдено в его reference
Вы часто также ручной реализации виртуальных таблиц, как в "COM in plain C"
EFraim - Не могли бы вы описать стратегию реализации GObject в своем ответе, чтобы мы могли сравнить ее с некоторыми другими решениями? – SetJmp
Посмотрите на IJG's реализации. Они не только используют setjmp/longjmp для обработки исключений, они имеют vtables и все такое. Это хорошо написанная и достаточно небольшая библиотека для вас, чтобы получить очень хороший пример.
Я стараюсь сделать что-то вроде этого:
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-like
foo_blah(foop, ...);
foo_plugh(foop, ...);
хотя, если придерживаться достаточно короткое имя для поля «Упс», просто выписывая код, указанный изначально не особо многословен.
Этот метод полностью подходит для реализации относительно простых объектных конструкций в C, но он не обрабатывает более сложные требования, такие как явно представляющие классы и наследование метода. Для них вам может понадобиться что-то вроде GObject (как упоминал EFraim), но я бы посоветовал убедиться, что вам действительно нужны дополнительные функции более сложных фреймворков.
Ваше использование термина «объекты» немного расплывчато, поэтому я собираюсь предположить, что вы спрашиваете, как использовать 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;
}
Теперь я свободно использовать полученные лишние поля, наряду с получением всех «унаследованными» полей базы. Кроме того, если у вас есть функция, которая принимает только структуру. вы можете просто нарисовать свой структурированный указатель на указатель структуры без каких-либо последствий.
Спасибо, Фалеан (я его поддержал). Не могли бы вы указать на библиотеку, которая делает это, чтобы я или кто-либо еще мог изучить целую реализацию? – SetJmp
Подобно подходу Dale, но немного более ногу, как PostgreSQL представляет узлы дерева разбора, типы выражений и т. д. внутренне. Есть по умолчанию Node
и Expr
Структуры, вдоль линий
typedef struct {
NodeTag n;
} Node;
где NodeTag
представляет собой ЬурейеЕ для неподписанных Int, и есть заголовочный файл с кучей констант, описывающих все возможные типы узлов. Узлы сами выглядеть следующим образом:
typedef struct {
NodeTag n = FOO_NODE;
/* other members go here */
} FooNode;
и FooNode
может быть приведён к Node
безнаказанно, из-за причуда структур C: если два имеют одинаковые Структуры первых членов, они могут быть отлиты друг к другу.
Да, это означает, что FooNode
можно отнести к BarNode
, который вы, вероятно, не хотите делать. Если вам нужна правильная проверка времени выполнения, GObject - это путь, хотя будьте готовы ненавидеть жизнь, пока вы ее повесите.
(примечание:. Примеры из памяти, я не взломаны на внутренних Postgres в то время как developer FAQ имеет больше информации.)
Долгое время назад (около 1988 года) я работал над компилятором C, где узлы синтаксического анализа были небольшим объединением отдельных типов узлов, с тегом типа вне союза, чтобы решить, какая ветвь объединения действительна: это морально эквивалентно тому, что вы описываете. Сегодня я почти наверняка сделал это в соответствии с моим предложением выше. Я считаю, что использование указателей функций действительно заставляет меня определять желаемый общедоступный api - поскольку вызывающий абонент не знает имя вызываемой функции, ему очень трудно достичь этой функции, чтобы использовать внутренние данные об этой реализации. –
Sigh ... s/bit union/большой союз/выше. Извините за опечатку. –
Метод указателя функции определенно превосходен - с одной стороны, он позволяет вам аппроксимировать методы привязки к объекту. Мне нравятся ваши примеры и проголосовали за это. –
Как вы можете видеть от просмотра ответов на все вопросы, есть библиотеки, указатели функций, средства наследования, инкапсуляция и т. д., все доступные (C++ первоначально был интерфейсом для C).
Однако я обнаружил, что ОЧЕНЬ важный аспект для программного обеспечения - . Вы пытались читать код с 10 лет назад? В результате , я, как правило, взять самый простой подход, делая такие вещи, как объектов в C.
Спросите следующее:
- Является ли это для клиента с крайним (если это так, рассмотрим ООП) ?
- Могу ли я использовать ООП (часто меньше кода, быстрее разрабатывать, более читаемо)?
- Могу ли я использовать библиотеку (существующий код, существующие шаблоны)?
- Я ограничена памятью или процессором (например, Arduino)?
- Есть ли еще одна техническая причина для использования C?
- Могу ли я сохранить свой C очень простым и удобочитаемым?
- Какие функции OOP мне действительно нужны для моего проекта?
Я обычно возвращаюсь к чему-то вроде 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
Хотя я всегда использовал то, что я думаю, вы могли бы назвать «объект на основе» стиль программирования C, это гораздо, гораздо проще в этом (и почти все остальное) в C++. Я должен спросить, почему вы, похоже, не хотите его использовать? – 2009-08-04 07:55:25
@Neil: Если бы не было причин, не было бы никакого GObject. Существует много причин для C: устаревших платформ, устойчивости, двоичной совместимости ABI, вы называете это. – EFraim
@EFraim Я хорошо осведомлен об этих причинах, но я спрашивал, каковы причины допроса. – 2009-08-04 08:57:31