Может ли структура C вести себя так, как будто у нее есть функция?

13

Я использую C и structs, где структура может иметь члены, но не функции. Предположим для простоты, что я хочу создать структуру для строк, которые я назвал, strи я хочу иметь возможность делать, str.replace(int i, char c)где iнаходится индекс строки и cсимвол, который заменяет символ в позиции i. Если бы это никогда не было возможно, так как структуры не могут иметь функции, или есть какой-то способ, которым мы можем реализовать это поведение и имитировать, что структура может иметь (простую) функцию, которая на самом деле является только структурой, копирующей себя в новую структуру и обновляющей ее поля, которые он мог сделать?

Таким образом, replaceможет быть третий член структуры, который указывает на новую структуру, которая обновляется при обращении к ней или аналогичной. Это могло быть сделано? Или есть что-то встроенное или какая-то теория или парадигма, которая мешает моему намерению?

Предпосылкой является то, что я пишу код на C и обнаруживаю, что заново изобретаю функции, которые, как я знаю, являются встроенными библиотеками в языках ООП, и что ООП будет хорошим способом манипулирования строками и командами.

Никлас
источник
5
Честно говоря, я думаю, что вам было бы лучше написать бесплатные функции для такого рода вещей. Тем не менее, если у вас есть необходимые смелости, прочитайте cs.rit.edu/~ats/books/ooc.pdf
Роберт Харви
5
Структуры могут включать переменные, которые являются указателями на функции. Нет встроенного наследования, но вы можете создать экземпляр своей структуры с помощью указателей, указывающих на разные функции с одинаковой сигнатурой. Вы будете часто хотеть сделать первый параметр функции указателем на структуру.
Джеймс Маклеод
29
действительно ли замена (& str, i, c) намного хуже, чем str.replace (i, c)? На самом деле ваш вопрос не о замене функций, а о попытке вставить новый синтаксис в C.
whatsisname
1
@RobertHarvey Спасибо за ссылку cs.rit.edu/~ats/books/ooc.pdf . Хорошая книга (и цена правильная).
Джон Форкош
3
@whatsisname: В C вы все равно должны передать структурный указатель на функцию, поэтому вы все равно получите str.replace(&str, i, c). thisКонечно, C ++ автоматизирует передачу указателя.
Джонатан Леффлер

Ответы:

21

Ваша функция должна выглядеть следующим образом.

void
replace(struct string * s, int i, char c);

Это принимает указатель на объект для работы в качестве первого параметра. В C ++ это называется this-pointer и не требует явного объявления. (Сравните это с Python, где это необходимо.)

Чтобы вызвать вашу функцию, вы также должны явно передать этот указатель. По сути, вы торгуете o.f(…)синтаксис для f(&o, …)синтаксиса. Не так уж и важно.

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

В качестве прокомментировал Ян Худек , вы также должны сделать привычкой добавлять префикс имени функции к имени типа (то есть string_replace), потому что в C нет пространств имен, поэтому может быть только одна названная функция replace.

5gon12eder
источник
17
Конечно, функцию, вероятно, придется вызывать string_replace, потому что в C тоже нет перегрузки функций, и у вас, скорее всего, есть другие replaceдля некоторых других типов ...
Ян Худек
2
Это не может быть названо string_replace. Имена , начинающиеся с str, memили с wcsпоследующим строчной буквой зарезервированы для расширения в будущем.
Дэвид Конрад
43

Структуры могут содержать функцию указатели на , но они действительно нужны только для виртуальных методов. Невиртуальные методы в объектно-ориентированном C обычно выполняются путем передачи структуры в качестве первого аргумента обычной функции. Посмотрите на Gobject для хорошего примера OOP-фреймворка для C. Он использует макросы для обработки большого количества шаблонов, необходимых для наследования и полиморфизма.

C был создан 44 года назад. Это очень популярный язык для открытого кода. Вы не первый, кто думает, что стандартные строки C неуклюжи для работы. Сделайте некоторые поиски для строковых библиотек C. Вам не нужно изобретать велосипед.

Карл Билефельдт
источник
2
Другой известный пример - CPython. В коде используется множество концепций ООП, но он на 100% чистый C.
Бакуриу
@Bakuriu Я думаю, что вы путаете Cython и CPython
кошка
1
@cat Он, вероятно, имеет в виду Python C API, Cython не на 100% чист. C. docs.python.org/c-api/intro.html
JAB
5
Посмотрите на источники CPython. Большинство вещей действительно выполняется с использованием парадигмы ООП, и они предоставляют API ООП, который в основном соответствует API Python.
Бакуриу
1
@Bakuriu О, вы имеете в виду среду исполнения Python, исходный код и C API, а не язык Python. Ваш комментарий не очень ясно дал понять
кошка
8

С помощью указателей функций вы можете сделать:

str.replace(&str, i, c);

Как правило, это полезно только в том случае, если реализация может измениться, и в этом случае вы должны использовать vtable, поэтому накладные расходы составляют только один указатель на структуру:

str.vtable->replace(&str, i, c);
o11c
источник
3
Я бы по-прежнему вызывал его как string_replace (& str, i, c), а затем использовал vtable внутри string_replace, а не узнавал бы сайт вызова о vtable.
Пит Киркхам,
2
@Pete Имена, начинающиеся с str(или memили wcs) и строчные буквы зарезервированы стандартом C для будущих расширений, поэтому не вызывайте его string_replace. str_replaceЭто хорошо.
Дэвид Конрад
3

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

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

Сообщение в блоге о не OO интерфейсах

Посмотрите, дает ли это вам какие-либо идеи.

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

Ричард Тирегрим
источник