Как разработать C / библиотека C++, чтобы быть применимым на многих клиентских языках? [закрытый]

Я планирую кодировать библиотеку, которая должна быть применимой большим количеством людей в на широком спектре платформ. Что я должен рассмотреть для разработки его право? Для создания это подвергает сомнению более конкретный, существует четыре "дополнительных вопроса" в конце.

Выбор языка

Рассматривая все известные требования и детали, я пришел к заключению, что библиотека, записанная в C или C++, была способом пойти. Я думаю, что основное использование моей библиотеки будет в программах, записанных в C, C++ и Java SE, но я могу также думать о причинах использовать его от ME Java, PHP.NET, Objective C, Python, Ruby, документов на получение удара, и т.д. Возможно, я не могу быть нацелен на всех них, но если это будет возможно, то я сделаю это.

Требования

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

  • Сама библиотека начнется маленький, но определенно вырастет до огромной сложности, таким образом, это не будет опция поддержать несколько версий параллельно.
  • Большая часть сложности будет скрыта в библиотеке, хотя
  • Библиотека создаст граф объектов, который используется в большой степени внутри. Некоторые клиенты библиотеки будут только интересоваться определенными атрибутами конкретных объектов, в то время как другие клиенты должны пересечь граф объектов в некотором роде
  • Клиенты могут изменить объекты, и библиотека должна быть уведомлена этого
  • Библиотека может изменить объекты, и клиент должен быть уведомлен этого, если она уже имеет дескриптор к тому объекту
  • Библиотека должна быть многопоточной, потому что она поддержит сетевые соединения с несколькими другими хостами
  • В то время как некоторые запросы к библиотеке могут быть обработаны синхронно, многие из них займут слишком много времени и должны быть обработаны в фоновом режиме и уведомить клиент относительно успеха (или отказ)

Конечно, ответы приветствуются, неважно, если они удовлетворяют мои конкретные требования, или если они отвечают на вопрос общим способом, который имеет значение для более широкой аудитории!

Мои предположения, до сих пор

Таким образом, вот некоторые мои предположения и заключения, которые я собрал в прошлых месяцах:

  • Внутренне я могу использовать то, что я хочу, например, C++ с перегрузкой оператора, множественным наследованием, обрабатываю метапрограммирование по шаблону..., пока существует портативный компилятор, который обрабатывает его (думайте о gcc / g ++),
  • Но мой интерфейс должен быть чистым интерфейсом C, который не включает искажение имени
  • Кроме того, я думаю, что мой интерфейс должен только состоять из функций, с основными / примитивными типами данных (и возможно указатели) передал как параметры и возвращаемые значения
  • Если я использую указатели, я думаю, что должен только использовать их, чтобы пасовать назад их к библиотеке, не воздействовать непосредственно на память, на которую ссылаются,
  • Для использования в приложении C++ я мог бы также предложить объектно-ориентированный интерфейс (Который является также склонным для именования искажения, таким образом, Приложение должно или использовать тот же компилятор или включать библиотеку в исходную форму),
  • Это также верно для использования в C#?
  • Для использования в Java SE / EE Java, применяется Собственный интерфейс Java (JNI). У меня есть некоторые элементарные знания об этом, но я должен определенно проверить его дважды.
  • Не все клиентские языки обрабатывают многопоточность хорошо, таким образом, должен быть единственный поток, говорящий с клиентом
  • Для использования на ME Java нет такой вещи как JNI, но я мог бы пойти с Вложенным VM
  • Для использования в сценариях Bash должен быть исполняемый файл с интерфейсом командной строки
  • Для других клиентских языков я понятия не имею
  • Для большинства клиентских языков было бы хорошо иметь вид интерфейса адаптера, записанного на том языке. Я думаю, что существуют инструменты для автоматической генерации этого для Java и некоторых других
  • Для объектно-ориентированных языков могло бы быть возможно создать объектно-ориентированный адаптер, который скрывает то, что интерфейс к библиотеке является базирующейся функцией - но я не знаю если стоящий усилия

Возможные дополнительные вопросы

  • действительно ли это возможно с управляемым усилием или является им просто слишком много мобильности?
  • есть ли какие-либо хорошие книги / веб-сайты об этом виде критериев расчета?
  • какое-либо из моих предположений неправильно?
  • какие библиотеки с открытым исходным кодом стоит изучить для приобретения знаний из их дизайна / интерфейс / источник?
  • meta: Этот вопрос довольно долог, Вы видите какой-либо способ разделить его на несколько меньших? (Если Вы отвечаете на это, сделайте это как комментарий, не как ответ),

43
задан Lena Schimmel 19 December 2009 в 13:15
поделиться

7 ответов

В основном верно. Прямой процедурный интерфейс - лучший. (что не совсем то же самое, что C btw(**), но достаточно близко)

I интерфейс DLLs много(*), как с открытым исходным кодом, так и с коммерческим, поэтому вот некоторые моменты, которые я помню из повседневной практики, обратите внимание, что это более рекомендуемые области для исследования, а не кардинальные истины:

  • Следите за декорациями и схожими "минорными" схемами манипуляций, особенно если вы используете компилятор MS. Особенно заметно, что соглашение stdcall иногда приводит к генерации декораций для ВБ (декорирование - это вещи типа @6 после имени символа функции)
  • Не все компиляторы могут на самом деле компоновать всевозможные структуры:
    • так что избегайте чрезмерного использования союзов.
    • избегайте битпакинга
    • и предпочтительно упаковывайте записи для 32-битного x86. Хотя теоретически это медленнее, по крайней мере, все компиляторы могут получить доступ к упакованным записям afaik, и официальные требования к выравниванию изменились со временем с развитием архитектуры
  • On Windows использовать stdcall. По умолчанию это используется для Windows DLL. Избегайте fastcall, он не полностью стандартизирован (особенно то, как передаются небольшие записи)
  • Некоторые советы по упрощению автоматического перевода заголовков:
    • макросы трудно конвертировать в автоконвертеры из-за их непечатности. Избегайте их, используйте функции
    • Определите отдельные типы для каждого типа указателя и не используйте составные типы (xtype **) в объявлениях функций. Насколько это возможно, следуйте мантре "определить перед использованием", это позволит избежать пользователей, которые переводят заголовки, чтобы перестроить их, если их язык в целом требует определения перед использованием, и облегчит однопроходные парсеры для их перевода. Или если им нужна контекстная информация для автоматического перевода.
  • Не выставляйте больше, чем необходимо. Если возможно, оставьте типы дескрипторов расплывчатыми. Это приведет только к проблемам с версионированием позже.
  • Не возвращайте структурированные типы, такие как записи/инструкции или массивы, в качестве возвратного типа функций.
  • всегда есть функция проверки версии (проще сделать различие).
  • Будьте осторожны с перечислениями и булевыми. Другие языки могут иметь несколько иные предположения. Их можно использовать, но хорошо документируйте, как они ведут себя и насколько они велики. Также подумайте и убедитесь, что перечисления не станут больше, если вы добавите несколько полей, разобьёте интерфейс. (Например, в Delphi/pascal по умолчанию булеаны 0 или 1, а другие значения неопределенны. Для C-подобных булеанов существуют специальные типы (размер байта, 16 бит или 32 бит, хотя изначально они были введены для COM, а не для C-интерфейса)
  • Я предпочитаю строковые типы, которые являются указателем на длину char + длину как отдельное поле (COM тоже так делает). Желательно не полагаться на нулевое завершение. Это не только по соображениям безопасности (переполнение), но и потому, что так проще/сложнее взаимодействовать с нативными типами Delphi.
  • Memory всегда создает API таким образом, чтобы поощрялось полное разделение управления памятью. IOW ничего не предполагает в управлении памятью. Это означает, что все структуры в вашей библиотеке выделяются через собственный менеджер памяти, и если функция передает вам структуру, то скопируйте ее, вместо того, чтобы хранить указатель, сделанный с помощью "клиентского" управления памятью. Поскольку вы рано или поздно случайно вызовете free или realloc на ней :-)
  • (язык реализации, а не интерфейс), неохотно меняйте маску исключений сопроцессора. Некоторые языки меняют это в соответствии со своими стандартами обработки ошибок с плавающей точкой(exception-).
  • Всегда устанавливайте пару обратных вызовов с настраиваемым пользователем контекстом. Это может быть использовано пользователем для передачи состояния обратного вызова без определения глобальных переменных. (например, экземпляр объекта)
  • будьте осторожны со словом состояния сопроцессора. Оно может быть изменено другими пользователями и сломать ваш код, а если вы его измените, то другой код может перестать работать. Слово статуса обычно не сохраняется/восстанавливается в рамках соглашений о вызове. По крайней мере, на практике
  • не используют параметры varargs в стиле Си. Не все языки допускают небезопасное использование переменного количества параметров. (*) Delphi-программист изо дня в день, работа, связанная с взаимодействием с большим количеством аппаратного обеспечения и, таким образом, перевод заголовков SDK производителя. По ночам разработчик Free Pascal, отвечающий, среди прочего, заголовки Windows.

(**) Это связано с тем, что то, что означает "C", все еще зависит от используемого компилятора Си, особенно если нет реальной универсальной системы ABI. Подумайте о таких вещах, как:

  • C добавление префикса подчеркивания в некоторых бинарных форматах (a.out, Coff?)
  • иногда разные компиляторы Си имеют разные мнения о том, что делать с небольшими структурами, передаваемыми по значению. Официально они не должны поддерживать это вообще afaik, но большинство поддерживают.
  • структурная упаковка иногда различается, так же как и детали вызова конвенций (например, пропуск целочисленные регистры или нет, если параметр регистрируется в FPU регистре)

===== автоматические преобразования заголовков ====

Хотя я не так хорошо знаю SWIG, я знаю и использую некоторые специфические для delphi заголовочные утилиты( h2pas, Darth/headconv и т.д.)

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

Обычно я делаю небольшой скрипт (в Pascal, но можно использовать что угодно с приличной поддержкой строк), который разбивает заголовок вверх, а затем пробую инструмент на относительно однородных частях (например, только структуры, или только определения и т.д.)

Затем я проверяю, нравится ли мне автоматический вывод преобразования, и либо использую его, либо пытаюсь сделать конкретный конвертер сам. Так как это для подмножества (например, только структуры), то часто это намного проще, чем сделать полный конвертер заголовков. Конечно, это немного зависит от того, что моя цель. (красивые, читаемые заголовки или быстро и грязно). На каждом шаге я могу сделать несколько замен (с помощью sed или редактора).

Самая сложная схема, которую я делал для заголовков Winapi commctrl и ActiveX/comctl. Там я объединил IDL и заголовок C (IDL для интерфейсов, которые представляют собой кучу недоступных макросов на C, заголовок C для остальных), и умудрился набрать макросы примерно на 80% (путем возврата шрифтов в макросах sendmessage обратно к макро-декларации, с разумными (wparam, lparam, lresult) значениями по умолчанию)

Полуавтоматический способ имеет недостаток в том, что порядок деклараций разный (e. g. сначала константы, затем структуры, затем объявления функций), что иногда делает обслуживание болезненным. Поэтому я всегда сохраняю оригинальные заголовки/dk для сравнения с.

Проект преобразования Jedi winapi может иметь больше информации, они перевели примерно половину заголовков окон в Delphi, и таким образом имеют огромный опыт.

.
30
ответ дан 26 November 2019 в 23:07
поделиться

Не знаю, но если это для Windows, то вы можете попробовать либо прямой C-подобный API (похожий на WINAPI), либо упаковать свой код как COM-компонент: потому что я догадываюсь, что языки программирования могут захотеть иметь возможность вызывать Windows API, и/или использовать COM-объекты.

.
4
ответ дан 26 November 2019 в 23:07
поделиться

Что касается автоматической генерации оберток, рассмотрим возможность использования SWIG. Для Java это сделает всю работу JNI. Кроме того, он способен корректно переводить сложные OO-C++-интерфейсы (при условии, что вы будете следовать некоторым основным рекомендациям, т.е. не будете использовать вложенные классы, не будете переусердствовать с шаблонами, плюс те, которые упомянул Марко ван де Воорт)

.
3
ответ дан 26 November 2019 в 23:07
поделиться

NestedVM, на мой взгляд, будет медленнее, чем чистая Java, из-за проверки границ массива на int[][], представляющей память виртуальной машины MIPS. Это такая хорошая концепция , но сейчас она может работать недостаточно хорошо (пока производители телефонов не добавят поддержку NestedVM (если они это сделают!), большинство вещей пока будут SLOW, n'est-ce pas)? В то время как он может быть в состоянии распаковать JPEG без ошибок, скорость не имеет большого значения! :)

Больше ничего из того, что вы написали, не означает, что это правильно или неправильно! Принципы звучат (в основном, просто слушая выбор слов и языка, если быть честным) как примерно стандартная лучшая практика, но я не продумал детали всего, что вы сказали. Как вы сами сказали, это действительно должно быть несколько вопросов. Но, конечно, делать такие вещи нелегко автоматически только потому, что вы исправлены, возможно, на архитектуре, немного отличающейся от последней кодовой базы, над которой вы работали...! ;)

Мои мысли:

Все ваши комментарии по совместимости интерфейса C звучат для меня разумно, в общем-то, лучшая практика, за исключением того, что вы, кажется, неправильно обращаетесь к политике управления памятью - некоторые предложения немного двусмысленны/ расплывчаты/неправильно звучали. Дизайн управления памятью будет в значительной степени определяться шаблонами доступа, сделанными в вашем приложении, а не функциональностью как таковой. Я уверен, что вы внимательно изучите попытки других сделать портативные интерфейсы, такие как стандартный ANSI C API, Unix API, Win32 API, Cocoa, J2SE и т.д.

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

.
2
ответ дан 26 November 2019 в 23:07
поделиться

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

Основная проблема, которую я вижу в этом, заключается в том, что люди выбирают конкретный язык и время его работы, потому что их образ мышления (функциональный или объектно-ориентированный) или проблема, к которой они обращаются (веб-программирование, базы данных...), так или иначе соответствует этому языку. Библиотека, реализованная на языке c, вероятно, никогда не будет похожа на библиотеки, к которым они привыкли, если только они сами не программируют на c. Лично я всегда предпочитал бы библиотеку, которая "чувствует себя как питон", когда я использую питон, и библиотеку, которая чувствует себя как java, когда я использую Java EE, хотя я знаю c и c++.

Так что ваши усилия могут быть малополезны (кроме как ваш опыт), потому что люди, вероятно, захотят придерживаться своего мышления, и вместо того, чтобы использовать библиотеку, которая делает работу, но не подходит.

Я также опасаюсь, что желаемая переносимость серьезно затруднит разработку. Просто подумайте о необходимых бесконечных настройках сборки, и тесты для этого. Я работал над проектом, который пытался поддерживать совместимость для 5 операционных систем (все posix-подобные, но всё же) и около 10 компиляторов, сборки были кошмаром для тестирования и сопровождения.

.
2
ответ дан 26 November 2019 в 23:07
поделиться

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

.
3
ответ дан 26 November 2019 в 23:07
поделиться

Дайте ему XML-интерфейс, переданный в качестве параметра и возвращающий значение или в виде файлов с помощью вызова командной строки. Это может показаться не таким прямым, как обычный интерфейс функции, но это наиболее практичный способ получить доступ к исполняемому файлу, например, с Java.

.
0
ответ дан 26 November 2019 в 23:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: