LINUX.ORG.RU

Сделать стабильный ABI для библиотеки и не повеситься

 , ,


1

3

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

  1. Шпилить её на чистой сишечке, немного страдать, но получить из коробки

  2. Или написать её на C++, страдая немного по-другому, но пользуясь всеми его благами? Однако реализуя тонкую extern "C" { } обёртку. Много бойлерплейта, но ничего глобально сложного.

Насколько я знаю, в таком случае ещё придётся конечную программу линковать компилятором С++, с этим уже не справится.

Насколько так вообще принято делать?

Ну на уровне библиотеки что там надо, интерфейсы функций не менять, да в структуры на будущее зарезервировать место пустое, один хрен что-то да надо будет добавить, вот добавишь, а ABI целое и невредимое останется, ну и всё. Шпилить на чистой сишечке библиотеки это всегда благо =)

Хотя может сейчас взрослые дяди придут и скажут что всё наоборот, гы

LINUX-ORG-RU ★★★★★
()

Ты путешь интероп и стабильный ABI.

Интероп это одно, и тут ты можешь писать на плюсах, через extern C предоставлять сишное API, никакого плюсового компилятора клиентам не нужно будет, понятно что ты не должен при этом давать исключениям покидать плюсового кода. Можешь так же писать и на расте и на ещё куче языков.

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

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

Спасибо за подробное хорошее объяснение! Действительно получалась некоторая путаница в понятиях.

Но все равно есть один момент: если я хочу, чтобы мою библиотеку можно было использовать из другого языка, будь то Си или Раст или даже питон/шарп — мне же в любом случае придётся писать экстерны?

Это в том числе и называется интеропом. А нормальный интероп с плюсами и растом, сколько мне известно, невозможен как раз по приче их нестабильного ABI.

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

И именно при таком юз кейсе, с каким из двух предложенных вариантов я отделаюсь меньшей болью?

witaway
() автор топика
Ответ на: комментарий от MOPKOBKA

Спасибо за пояснение!

Получается, если мне не претит написать эти бойлерплейты, больше я при написании библиотеки на плюсах ни с какими проблемами совместимости не столкнусь?

Иначе говоря, соберу её плюсовым компилятором — и потом бесшовно куда захочу, в софт на каком угодно языке и с каким угодно компилятором, при условии возможности языка Си-интеропа, смогу её интегрировать?

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

А вообще, что мешает плюсу и тому же расту иметь стабильный ABI? Был бы он, то и интероп в другие приличные языки уже так или иначе, но давно бы прикрутили. Что делает Си в этом плане немного особенным?

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

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

Пример абсолютно неприемлемого и ублюдочного подхода к написанию библиотеки - icu4c, которая полностью ломает ABI при любых минорных обновлениях, выдавая из за мажорные, из-за чего приходится пересобирать тонны софта когда меинтейнеру icu4c приспичло обновить её. Вот так делать не надо ни в коем случае.

Пример грамотной реализации библиотеки - libusb например. Накосячили в очередном релизе с какими-нибудь составными девайсами - никаких проблем, подсунул даже новому софту бородатую версию - и всё прекрасно работает. Вышла пофикшенная версия - обновил, а софт ничего не заметил, ни собранный со старой версией, ни собранный с новой.

Вкратце - если есть некая libzzzz.so, то для софта слинкованного c libzzzz.so.1 не должно быть вообще никакой принципиальной разницы между libzzzz.so.1.23.198 и libzzz.so.1.0.0 в смысле работоспособности. Софт собранный c libzzzz.so.1.23.198 должен без проблем нормально работать с libzzzz.so.1.0.0 и наоборот.

То же самое относится и к использованию либ самой libzzzz.so. Если минорное обновление libzzzz.so требует обновить какую-нибудь либу на которую завязано 100500 другого софта, типо той же проклятущей icu4c, то это западло и гейство в самом плохом смысле.

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

В идеале, конечно, у любого создателя либы должна стоять задача написать либу которую вообще не надо будет апгрейдить никогда. Релиз 1.0.0 должен быть единственным и последним. Цель, конечно недостижимая, но именно к ней надо стремиться. :)

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

COM на линуксах нет (ну, кроме одного, вроде бы заброшенного проекта) - так что нещитово.

А вот как pyside6 работает я, честно говоря, не в курсе. Судя по описанию, оно как-то генерирует биндинги:

PySide6 versions following 6.0 use a C++ parser based on Clang. The Clang library (C-bindings), version 13.0 or higher is required for building. Prebuilt versions of it can be downloaded from download.qt.io.

Открыл исходники растового крейта - и там вроде бы прямой интероп с C++, но тоже применяется какой-то очень сложный генератор, который создаёт код примерно следующего вида:

QColor
qcolorInitFromRgb(::std::int32_t red,
                  ::std::int32_t green,
                  ::std::int32_t blue,
                  ::std::int32_t alpha)
{
  return QColor::fromRgb(red, green, blue, alpha);
}
#[cxx::bridge]
mod ffi {
    
    ...

    #[namespace = "rust::cxxqtlib1"]
    unsafe extern "C++" {
        type QColorNameFormat;
        type QColorSpec;

        ...

        #[doc(hidden)]
        #[rust_name = "qcolor_init_from_rgb"]
        fn qcolorInitFromRgb(red: i32, green: i32, blue: i32, alpha: i32) -> QColor;

        ...
    }

}

В общем, как-то совершенно неочевидно!

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

Ты всё ещё путаешься в терминах. Нестабильность ABI возможна только во времени, когда A меняется а B нет, где A и B это пара из [язык библиотеки, язык приложения, версия библиотеки, версия приложения]. Если взять всё из одной точки во времени, что обычно и происходит (когда собирается пакетный репозиторий, или блобопакет) никакой нестабильности нет и быть не может.

А интероп про который ты говоришь, это не ABI вообще, а API. Ну и да, тут нужно маппить структуры одного языка в структуры другого, хотя для этого есть куча средств, но это всё-таки нужно делать, без этого никак. Однако никакой принципиальной проблемы тут нет - бери и юзай librsvg2 написанный на rust из c++ хоть через питон в котором зовёшь lua.

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

Так это вы говорите о том, что не надо писать блядские интерфейсы. Оно-то вроде бы и понятно. Не уверен, что у меня руки в достаточной мере далеки от жопы, но следовать подобным советам и гайдлайнам всегда пытаюсь.

У меня сложность возникает с пониманием, как сделать нормальный интероп для какой-либо разрабатываемой библиотеки.

С сишечкой всё понятно и просто. С ней умеют работать все. Но вот:

  1. Интероп с либой на Си++ - это как?
  2. Интероп, коль на то пошло, с либой на Расте - это как?

Я вроде бы и посмотрел, как это делается в растовском крейте для кутей - и там ничего очевидного. Какие-то странные генераторы С++ кода в С++ код, ничего не понимаю - хотя интероп с Си++ в Расте, говорят, заявлен.

Потом прихожу в интернет - и вижу вой на тему сложности интеропа с этими языками по причине нестабильного ABI именно что генерируемого компилятором кода.

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

Да даже линковка библиотеки, собранной одной версией компилятора C++ к программе, собранной другой версией компилятора очень легко может отвалиться.

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

А интероп про который ты говоришь, это не ABI вообще, а API

Подожди. Ясное дело, что обёртка, которую ты пишешь на другом языке это API. Но разве в процессе самого импорта методов в другой язык он не должен знать внутренние соглашения и структуры языка целевой библиотеки? Это, сколько понимаю, тоже вопрос ABI.

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

В тех же самых растах, я глянул, нужно для этого писать

#[no_mangle]
pub extern "C" fn rust_function() -> i32 {
    42
}

Тем более, если линковка у нас не статическая, а динамическая…

Честно, я до конца не вдупляю до конца твою мысль.

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

У меня сложность возникает с пониманием, как сделать нормальный интероп для какой-либо разрабатываемой библиотеки.

Можешь сделать ленивый C API не заморачиваясь со структурами, сделай так чтоб в интерфейсе были либо указатели на структуры/void/примитивные типы и работу с ними через наборы функций.

Например что-то типа такого:


struct MyString
{
    QString string;
};

extern "C" {
    //Forward declaration
    struct MyString;

    MyString* createString()
    {
        return new MyString();
    }

    void freeString(MyString* string)
    {
        delete string;
    }
    
    void setStringText(MyString* string, chat* text, int size)
    {
        string->string = QString::fromUtf8(text, size);
    }
    
    void concatString(MyString* dst, MyString* src)
    {
        dst->string.append(src->string);
    }
}
V1KT0P ★★
()
Ответ на: комментарий от witaway

Но вот:

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

Конечно, если твоя либа является частью какого-нибудь фреймворка на чём-то отличном от C и не предполагает использования вне этой экосистемы, то это другой разговор, но в данном случае ведь речь идёт об универсальной библиотеке, насколько я понял.

Да даже линковка библиотеки, собранной одной версией компилятора C++

Это только в том случае, если ABI С++ное. Если ABI plain C никаких проблем нет вообще, даже если либа написана на фортране, C++ и расте одновременно. Главное чтобы наружу торчал только и исключительно стабильный интерфейс в виде plain С функций, структур и констант. И волосы будут гладкие и шелковистые.

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

Именно это имею в виду. Правильно понимаю, что если надо дёшево, просто и универсально, то такой подход всё-таки единственный верный?

Сейчас бегло глянул, оказалось, что в том же расте есть прямой интероп из С++, но он, честно говоря, я вообще не представляю, как и почему работает — и имеет свои ограничения.

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

Как только выходишь из простого мира Си и сишных врапперов — оказываешься в каком-то нестабильном и довольно сложном мире.

Как будто в моих знаниях какая-то очень большая дырка.

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

Именно это имею в виду. Правильно понимаю, что если надо дёшево, просто и универсально, то такой подход всё-таки единственный верный?

Да, это самый удобный способ, пока логика API не будет нарушена можно что угодно делать внутри с C++ кодом, хоть потом заменить на Rust. Уже на других языках поверх этого API можно сделать удобный wrapper.

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

Получается, пишу на каком угодно языке средне-низкого уровня, то, что надо, оборачиваю в экстерны, чтобы получить plain C интерфейс — и вызываю уже откуда хочу? И больше мне вообще ни о чем думать не нужно.

В данном случае речь о C++, но при желании ищу аналогичный механизм в Расте, Фортране, Паскале и так далее.

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

В Расте есть, вроде бы, изкоробочная поддержка без посреднтюичества в виде сишных интерфейсов.

У кутей вообще всё как будто довольно сложно и они в раст крейтк используют какие-то генераторы из С++ в С++ (хотя, казалось бы, зачем?)

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

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

Это отлично.

Вроде бы задал вопрос, на который и так знал ответ. Но хотя бы нормально убедился, что такое решение самое простое и надёжное. Спасибо!

Правда теперь побочных вопросов стало только больше. Вроде приведенных выше. Вне сишных интерфейсов уровень необходимых для понимания происходящего знаний возрастает. И куда копать пока тоже не понимаю. :)

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

Получается, пишу на каком угодно языке средне-низкого уровня, то, что надо, оборачиваю в экстерны, чтобы получить plain C интерфейс — и вызываю уже откуда хочу?

Именно. С calling convention это стандарт де-факто как для библиотек, так и для их системных загрузчиков. С++ ABI это скорее костыльное использование этого стандартного механизма посредством закостыливания С++ плюшек в имена экспортируемых функций, типа _ZN7QString6appendE13QLatin1String вместо QString::append(QLatin1String). C++ ABI на самом деле тоже plain C ABI, С++ методы в либах можно без особых проблем дёргать из plain C кода, да и любого другого. Просто при малейших правках например изменения типа аргумента с int на long или добавления const у тебя меняется название функций, и они становятся недоступны для софта слинкованного с предыдущей версией, в отличии от plain C.

Так что да, заворачивай всё в чисто сишную обёртку и выставляй её наружу.

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

Херово. Потому то см. выше - «интерфейс плюсов» это на самом деле, сишный интерфейс с костылями, только и всего. И эти костыли зависят от используемого стандарта плюсов, реализации и погоды на марсе. И если из сишечки ты хоть как-то можешь вручную дёргать плюсовые методы спускаясь на уровень vtable, указателей и пр, то в других языках может вообще не быть способа дёрнуть тот или иной костылик в плюсовой либе.

Пользователи других языков сами запросто напишут себе прокладку для сишной либы если либа им понадобятся. Практически во всех языках есть возможность использовать сишные либы. Зачем заморачиваться с чем-то другим? Ничего кроме геморроя не получится.

Так-то можно вообще каких-нибудь своих костылей нагородить в ABI, каких-нибудь специфически паскалевых или растовых, например. Но кто тогда захочет связываться с этим геморроем?

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

В питоне всё очень просто вроде же. Есть аж 2 варианта - ctype и CFFI. грузишь либу (что-то типа lib = ctype.cdll("libzzzz.so") или lib = ffi.dlopen("libzzzz.so") ) и после несложного объяснения питономодулю какую именно функцию тебе надо (имя, типы аргументов и позвращаемого значение), можешь потом вызывать эту сишную библиотечную функцию её в своём питонокоде без всяких проблем, так же как питонофункции. Буквально несколько строк нужно.

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

А вот если библиотека - часть фреймворка и без него не имеет смысла, например какой-нибудь Qtшный модуль, то разумеется она должна быть написана в соответствиями с принятыми во фреймворке соглашениями чтобы не выбиваться из общей картины.

Stanson ★★★★★
()

можешь сразу делать интерфейс SWIG (https://swig.org) для своей библиотеки, чем сильно упростишь людям «интероп». Благо что это несложно и тебе как автору библиотеки удобнее чем другим.

при этом «шпилить» можно и С++, но без излишнего фанатизма с шаблонами

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

Иначе говоря, соберу её плюсовым компилятором — и потом бесшовно куда захочу, в софт на каком угодно языке и с каким угодно компилятором, при условии возможности языка Си-интеропа, смогу её интегрировать?

Да.

MOPKOBKA ★★★★★
()

Насколько так вообще принято делать?

Да кроме десятка олдскульных базовых библиотек никто сейчас не заморачивается с ABI в энтерпрайзе. Толпы вкатышей даже слов таких не знают, не подозревают о существовании таких проблем. Оборачивают докеры в докерах на виртуалке, используют хттп для локалхост-онли IPC, который пишет в кафку джобы, выполняющие sql запрос в таблицу постгреса, в котором хранят конфиг на максимум 20 килобайт.

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

Спасибо! Теперь более полно понимаю, что же с плюсовыми интерфейсами не так. Это здорово помогает.

В питоне всё очень просто вроде же. Есть аж 2 варианта - ctype и CFFI.

Тут дело в том, что данные инструменты предназначены как раз для plain C. А куте плюсовое. Ну, наверное как раз потому они и заставляют кутей при каждой установке модуля компилировать заново.

И кути, сколько я понимаю, не экспортируют вот так сразу напрямую сишные символы.

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

Если библиотека общего назначения - то только plain C интерфейс без вариантов.

Я это очень чётко и конкретно понимаю.

Хм. Просто за время, пока я рыскал по открытым репозиториям, в основном находил plain C библиотеки. А чтобы библиотека конкретно на плюсах и экспортирует сишные символы — уже по какой-то причине редкость.

Знаю только один достаточно популярный контрпример — SFML. У них есть отдельный репозиторий с си-биндингами.

Собственно, это во мне и породило сомнения: «А почему так? А может это неспроста? Может я что-то упускаю и чистый Си окажется путём наименьшего сопротивления?»

witaway
() автор топика
Ответ на: комментарий от snizovtsev

Да я и сам своего рода вкатыш. Только университет закончил, диплом ещё на руки получить не успел. Буду, правда, отрабатывать свою двухгодовую повинность не на галере с нодами и шарпами, а на заводе с одинэсочкой.

Они и понятно, почему так делают — экономят времяденьги своего заказчика. Зато станет IPC не локалхост-онли, а у тебя уже настроен какой-нибудь gRPC. Надо будет безболезненно развернуть софтину на незнакомой машине (или даже парке оных) — а у тебя и докер-контейнер подготовлен.

Просто не работает это всё с системным софтом. Да и хочется, чтобы было хорошо и оптимально. Да и хочется иногда наваять чего-нибудь своего.

Ещё на работу выйти и там выгореть не успел, а от крудобекендов уже в уныние тянет.

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

А вообще, что мешает плюсу и тому же расту иметь стабильный ABI?

В плюсах и так стабильный ABI. Более того, он и в плюсовых стдлибах стабильный, причём настолько стабильный, что от этого все плюются.

Что делает Си в этом плане немного особенным?

Простота, доходящая до аскетичности.

intelfx ★★★★★
()
  1. Стабильный ABI нужен только тогда, когда у твоей библиотеки есть пользователи. А у тебя они есть? Что такое пилишь, если не секрет? :)

  2. Второй вариант удобнее, но можно и пойти дальше, то есть пользоваться почти всеми благами C++, иметь стабильное ABI, и без extern "C". Главное на границе не передавать объекты, стабильность структуры которых ты гарантировать не можешь. Взять тот же std::string, например. До C++11 и после это два разных объекта. Насколько я помню, у glibcxx даже специальный макрос есть переключающий его формат.

  3. Добавлю к тому, что выше. Для интеропа с языками, которые поддерживают только Си можно так-то написать отдельную обертку.

  4. Не забывай про существование SONAME. Да, он немного ограниченный, но хотя бы немного упрощает сосуществование нескольких версий твоей библиотеки, на случай если ты поменяешь ABI.

a1ba ★★
()
Последнее исправление: a1ba (всего исправлений: 2)
Ответ на: комментарий от Dr64h

Спасибо. Тоже узнал кое-что новое. Буду применять, когда необходимо.

Что интересно, в сях, засчёт простоты языка, этот паттерн более очевидный. Я его даже и реализовывал. В плюсах бы пропустил как есть. Наверное, потому что пока не хватает системного понимания проблемы.

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

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

Бинарные не системные библиотеки нужны только лишь в очень редких сценариях:

  1. для горячей подгрузки кода в без перезапуска;
  2. подключения труда команды пилящей что-то очень секретное, при этом требующее очень быстрого или сложного интерфейса, который сложно заменить на IPC.

Вообщем, если ты новичёк и задумался об ABI, то подумай лучше как организовать работу так, чтобы не думать об ABI.

snizovtsev ★★★★★
()
Последнее исправление: snizovtsev (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Если он действительно для завода разрабатывает - то за сишечку «подопытные тестеры» на сдельщине, его будут благодарить ботиночками с металлическими «подносками»))) 💥️🙀️

Пацаны рассказывали..

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

Пользователей нет и не факт, что будут. И реализация пока на уровне изучения путей решения проблемы. Но это же не мешает мне уже сейчас разобраться, как делать более правильно и совместимо? Иначе так можно и на всю жизнь остаться обезьяной. А я люблю понимать, что делаю :)

Думается мне, что пункты 2 и 3 немного экзотика. Как будто больше шансов что-то поломать. Да и языков, которые умеют надёжно интеропиться только с Си заметно больше.

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

Предложенный в пункте 2 вариант ещё как будто оставляет больше путей по невнимательности прострелить себе ногу. Вот прям нутром чую, что это скользкая дорожка.

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

Не для завода, а для души! В свой собственный репозиторий. Заводчан, так и быть, пожалею.

Это я просто так написал, немного пожаловаться на свою судьбинушку. Хочется просто решать нормальные задачи, а не это всё.

Помнится, до того, как начал ковырять одинэс, думал: «ща как всё по красоте начну делать, применю свои знания о том, как писать поддерживаемый расширяемый код — будет красота!» — но как-то не срослось. Одинэс вроде как не хочет, чтобы ты писал хороший код.

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

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

И кути, сколько я понимаю, не экспортируют вот так сразу напрямую сишные символы.

Именно сишные символы с плюсовыми костылями они напрямую и экспортируют, Я выше пример же прям из живой libQtCore.so привёл, как это выглядит. Все эти _ZN7Q и E13Q вместе с типом аргумента не просто случайный набор букв, а вполне однозначная информация о всяких модификаторах, возвращаемом значении, размере аргументов и пр. Т.е. это и есть эти плюсовые костылики поверх сишного ABI.

Тут дело в том, что данные инструменты предназначены как раз для plain C. А куте плюсовое.

Вообще, можно и через инструменты для plain C, но это будет большой геморрой с жонглированием vtbl, this и пр. вручную. Поэтому тот же питон имеет специальный модуль pyQt или как он там, написанный на плюсах специально для интерфейса к Qt. Сделать какой-то универсальный линкер плюсовых библиотек - задача непростая, проще каждый раз для каждой плюсовой либы писать специальный модуль.

Ну, наверное как раз потому они и заставляют кутей при каждой установке модуля компилировать заново.

Это связано скорее с вот этими костыликами в экспортируемых именах - _ZN7Q и E13Q в примере. Добавили в минорном релизе кутей забытый const или там QLatinString размером вырос и всё - symbol _ZN7QString6appendE13QLatin1String not found in libQtCore.so. Поэтому при каждом обновлении Qt желательно пересобрать всё связанное с ним.

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

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

Поинт понял. Разработчики Qt как будто пошли не по самому очевидному и простому пути. Что им могло помешать написать для библиотеки биндинг с сишными экстернами, а биндинги для всех прочих языков строить поверх сишного? Как будто бы всё сразу стало намного проще.

Хотя, если подумать, у gobject и, соответственно, GTK тоже всё весьма интересно. Эти, в свою очередь, каким-то образом реализуют интроспекцию gobject-классов, генерируют файлы-схемы, а потом из других языков сторонние разработчики тоже пишут генераторы биндингов, но уже обратно из этих схем. (кстати, очень жаль, что GTK# помер)

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

Я предложил именно вариант изначально всё делать на плюсах, потому что с библиотекой будет проще работать из самих же плюсов. Звучит логично, я знаю. :)

Если изначально оглядываться на возможность оборачивать всё Си, то это не так сложно.

Предложенный в пункте 2 вариант ещё как будто оставляет больше путей по невнимательности прострелить себе ногу.

Зато жить веселее!

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

Потому что Qt изначально рассчитана исключительно на написание ООП софта на C++. Сишные биндинги были бы крайне неудобными для использования, или слишком сложными для полноценной реализации. Понадобилось бы не просто биндинг написать, а целый новый фреймворк, устроенный совсем иначе, типа gtk который использовал бы Qt вместо X11/glibc или win32 внутри.

У trolltech не было задачи создать библиотеку типа какой-нибудь libpng или libz для ширпотребного использования где попало, они делали конкретно фреймворк заточенный на создание ООП GUI приложений на C++.

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

Я так-то вообще по окончанию отработки хочу сбежать из мира одинэсов в мир нормальной разработки. Не для того мама ягодку растила. Как вспоминаю про «общие модули», так унывать начинаю ))

Читал ещё, как 1с разработчики рекордами и их принимающими методами пытаются имитировать что-то вроде ООП, только без ООП. Или вовсе пишут в классическом структурном процедурном стиле.

В официальных конфигурациях тоже, как я понимаю, тонна сомнительных хаков.

Хочу пилить что-то нормальное и приятное .

witaway
() автор топика
Последнее исправление: witaway (всего исправлений: 2)
Ответ на: комментарий от Stanson

А в чём избыточная сложность? В растовом крейте у них лежит практически что тот же самый сишный биндинг, только, судя по всему, автоматически генерируемый. Тас они отколупывают плюсовый ООП и шаблоны — а потом импортируют это в Раст. Делают как будто почти ровно то, о чём я и говорю, но немного сложнее.

witaway
() автор топика

Насколько я знаю, в таком случае ещё придётся конечную программу линковать компилятором С++, с этим уже не справится.

Кто не справится? Компилятор C++ с линкованием не справится?

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

Интероп, коль на то пошло, с либой на Расте - это как?

Легко и просто. Я раст к питону прикручивал, он мне так даже больше сишки понравился, т.к. строки там куда ближе к питновским. Но на это в растишке кажись свои костылики были, чтоб ручками меньше обезьяньей работы делать, если заведомо известно что пилим проект под python, всякие там декораторы #[pyfunction] специально завозили под это дело. Что там внутри я не смотрел особо, может сишный интерфейс лепится, а может что-то более оптимальное.

peregrine ★★★★★
()