Для примера я взял открытую C++ библиотеку Open Babel для работы с моделями молекул. API библиотеки необъятно, поэтому решено было сделать только ту привязку, которая необходима для тестовой задачи — чтения молекулы из файла и вывод координат её атомов. Так как для ввода Open Babel использует стандартные классы С++, то в привязку было добавлены функции открытия и закрытия std::ifstream
Для удобства использования я создал структуру каталогов для привязки:
$ mkdir -p openbabel/singularity/definition openbabel/implementationВ таком виде библиотеку проще подключать к транслятору ключом
-infr openbabel
Где создал интерфейсный модуль на Обероне openbabel/singularity/definition/OpenBabel.mod
:
MODULE OpenBabel; TYPE Atom * = POINTER TO RECORD END; Mol * = RECORD END; Conversion* = RECORD END; Ifstream * = POINTER TO RECORD END; PROCEDURE Open*(name: ARRAY OF CHAR): Ifstream; BEGIN ASSERT(name # ""); RETURN NIL END Open; PROCEDURE Close*(VAR s: Ifstream); BEGIN s := NIL END Close; PROCEDURE SetInFormat*(VAR c: Conversion; fmt: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END SetInFormat; PROCEDURE Read*(VAR m: Mol; VAR c: Conversion; in: Ifstream): BOOLEAN; RETURN FALSE END Read; PROCEDURE GetAtom*(m: Mol; idx: INTEGER): Atom; RETURN NIL END GetAtom; PROCEDURE GetVector*(a: Atom; VAR v: ARRAY OF REAL); BEGIN ASSERT(LEN(v) >= 3); ASSERT(FALSE) END GetVector; END OpenBabel.
Транслировал его в Си-код
$ ost to-c OpenBabel openbabel/singularity/implementation -m openbabel/singularity/definition
Получив такие два файла-болванки для вставки кода:
- openbabel/singularity/implementation/OpenBabel.h
#if !defined HEADER_GUARD_OpenBabel # define HEADER_GUARD_OpenBabel 1 typedef struct OpenBabel_Atom__s { char nothing; } *OpenBabel_Atom; #define OpenBabel_Atom__s_tag o7_base_tag extern void OpenBabel_Atom__s_undef(struct OpenBabel_Atom__s *r); typedef struct OpenBabel_Mol { char nothing; } OpenBabel_Mol; #define OpenBabel_Mol_tag o7_base_tag extern void OpenBabel_Mol_undef(struct OpenBabel_Mol *r); typedef struct OpenBabel_Conversion { char nothing; } OpenBabel_Conversion; #define OpenBabel_Conversion_tag o7_base_tag extern void OpenBabel_Conversion_undef(struct OpenBabel_Conversion *r); typedef struct OpenBabel_Ifstream__s { char nothing; } *OpenBabel_Ifstream; #define OpenBabel_Ifstream__s_tag o7_base_tag extern void OpenBabel_Ifstream__s_undef(struct OpenBabel_Ifstream__s *r); extern struct OpenBabel_Ifstream__s *OpenBabel_Open(o7_int_t name_len0, o7_char name[/*len0*/]); extern void OpenBabel_Close(struct OpenBabel_Ifstream__s **s); extern o7_bool OpenBabel_SetInFormat(struct OpenBabel_Conversion *c, o7_int_t fmt_len0, o7_char fmt[/*len0*/]); extern o7_bool OpenBabel_Read(struct OpenBabel_Mol *m, struct OpenBabel_Conversion *c, struct OpenBabel_Ifstream__s *in_); extern struct OpenBabel_Atom__s *OpenBabel_GetAtom(struct OpenBabel_Mol *m, o7_int_t idx); extern void OpenBabel_GetVector(struct OpenBabel_Atom__s *a, o7_int_t v_len0, double v[/*len0*/]); extern void OpenBabel_init(void); #endif
- openbabel/singularity/implementation/OpenBabel.c
#include <o7.h> #include "OpenBabel.h" #define OpenBabel_Atom__s_tag o7_base_tag extern void OpenBabel_Atom__s_undef(struct OpenBabel_Atom__s *r) { } #define OpenBabel_Mol_tag o7_base_tag extern void OpenBabel_Mol_undef(struct OpenBabel_Mol *r) { } #define OpenBabel_Conversion_tag o7_base_tag extern void OpenBabel_Conversion_undef(struct OpenBabel_Conversion *r) { } #define OpenBabel_Ifstream__s_tag o7_base_tag extern void OpenBabel_Ifstream__s_undef(struct OpenBabel_Ifstream__s *r) { } extern struct OpenBabel_Ifstream__s *OpenBabel_Open(o7_int_t name_len0, o7_char name[/*len0*/]) { O7_ASSERT(o7_strcmp(name_len0, name, 0, (o7_char *)"") != 0); return NULL; } extern void OpenBabel_Close(struct OpenBabel_Ifstream__s **s) { (*s) = NULL; } extern o7_bool OpenBabel_SetInFormat(struct OpenBabel_Conversion *c, o7_int_t fmt_len0, o7_char fmt[/*len0*/]) { return (0 > 1); } extern o7_bool OpenBabel_Read(struct OpenBabel_Mol *m, struct OpenBabel_Conversion *c, struct OpenBabel_Ifstream__s *in_) { return (0 > 1); } extern struct OpenBabel_Atom__s *OpenBabel_GetAtom(struct OpenBabel_Mol *m, o7_int_t idx) { return NULL; } extern void OpenBabel_GetVector(struct OpenBabel_Atom__s *a, o7_int_t v_len0, double v[/*len0*/]) { O7_ASSERT(v_len0 >= 3); O7_ASSERT((0 > 1)); } extern void OpenBabel_init(void) { static unsigned initialized = 0; if (0 == initialized) { } ++initialized; }
Для уменьшения накладных расходов на привязку я избавился от .c файла, оставив только заголовочный файл, пометив его функции как встраиваемые и наполнив нужным кодом
#if !defined HEADER_GUARD_OpenBabel # define HEADER_GUARD_OpenBabel 1 #include <iostream> #include <fstream> #include <openbabel/mol.h> #include <openbabel/obconversion.h> typedef struct OpenBabel_Atom__s { OpenBabel::OBAtom a; } *OpenBabel_Atom; #define OpenBabel_Atom__s_tag o7_base_tag O7_ALWAYS_INLINE void OpenBabel_Atom__s_undef(OpenBabel_Atom *r) {} typedef struct OpenBabel_Mol { OpenBabel::OBMol m; } OpenBabel_Mol; #define OpenBabel_Mol_tag o7_base_tag O7_ALWAYS_INLINE void OpenBabel_Mol_undef(OpenBabel_Mol *r) {} typedef struct OpenBabel_Conversion { OpenBabel::OBConversion c; } OpenBabel_Conversion; #define OpenBabel_Conversion_tag o7_base_tag O7_ALWAYS_INLINE void OpenBabel_Conversion_undef(OpenBabel_Conversion *r) {} typedef struct OpenBabel_Ifstream__s { std::ifstream s; } *OpenBabel_Ifstream; #define OpenBabel_Ifstream__s_tag o7_base_tag O7_ALWAYS_INLINE void OpenBabel_Ifstream__s_undef(OpenBabel_Ifstream *r) {} O7_ALWAYS_INLINE OpenBabel_Ifstream OpenBabel_Open(o7_int_t len, o7_char name[/*len*/]) { OpenBabel_Ifstream f; O7_ASSERT(name[0] != 0); f = (OpenBabel_Ifstream)new std::ifstream((char *)name); return f; } O7_ALWAYS_INLINE void OpenBabel_Close(OpenBabel_Ifstream *s) { O7_ASSERT(*s != NULL); ((std::ifstream *)(*s))->close(); delete (std::ifstream *)*s; *s = NULL; } O7_ALWAYS_INLINE o7_bool OpenBabel_SetInFormat(OpenBabel_Conversion *c, o7_int_t len, o7_char fmt[/*len*/]) { return c->c.SetInAndOutFormats((char *)fmt, (char *)fmt); } O7_ALWAYS_INLINE o7_bool OpenBabel_Read(OpenBabel_Mol *m, OpenBabel_Conversion *c, OpenBabel_Ifstream in) { return c->c.Read(&m->m, (std::ifstream *)in); } O7_ALWAYS_INLINE OpenBabel_Atom OpenBabel_GetAtom(OpenBabel_Mol *m, o7_int_t idx) { return (OpenBabel_Atom)m->m.GetAtom(idx); } O7_ALWAYS_INLINE void OpenBabel_GetVector(OpenBabel_Atom a, o7_int_t len, double out[/*len*/]) { O7_ASSERT(len >= 3); OpenBabel::vector3 v; v = a->a.GetVector(); v.Get(out); } O7_ALWAYS_INLINE void OpenBabel_init(void) {} #endif
Для тестирования привязки был написан модуль Mol.mod для выполнения задачи в иходной постановке:
MODULE Mol; IMPORT Out, Ob := OpenBabel; PROCEDURE Read*(VAR m: Ob.Mol; name: ARRAY OF CHAR); VAR conv: Ob.Conversion; in: Ob.Ifstream; ok: BOOLEAN; BEGIN IF Ob.SetInFormat(conv, "xyz") THEN in := Ob.Open(name); ok := Ob.Read(m, conv, in); Ob.Close(in) END END Read; PROCEDURE OutAtom(a: Ob.Atom); VAR r: ARRAY 3 OF REAL; i: INTEGER; BEGIN Ob.GetVector(a, r); FOR i := 0 TO LEN(r) - 1 DO Out.Real(r[i], 0); Out.String(" ") END END OutAtom; PROCEDURE Log*(n: ARRAY OF CHAR); VAR mol: Ob.Mol; atom: Ob.Atom; i: INTEGER; BEGIN Read(mol, n); i := 1; Out.String("Molecule: "); Out.Ln; atom := Ob.GetAtom(mol, i); WHILE atom # NIL DO Out.Int(i, 0); Out.String(") "); OutAtom(atom); Out.Ln; INC(i); atom := Ob.GetAtom(mol, i) END END Log; END Mol.
Такой модуль можно было бы, к примеру, запустить командой:
$ ost run 'Mol.Log("water.xyz")' -infr openbabel -m . \ -cc "g++ -xc++ -I/usr/include/openbabel-2.0 -lopenbabel"
water.xyz
3 O -0.00000 -0.35107 -0.00000 H -0.81100 0.17553 0.00000 H 0.81100 0.17553 0.0000
Но здесь проявилась особенность компилятора g++ - опция компоновщика -lopenbabel игнорируется, если указана до имён файлов с исходным кодом. Для возможности разделения опций компилятора Си необходимо доработать транслятор ost. Пока же нужно отдельно транслировать код на Обероне в Си, затем запускать компилятор C++, после чего можно запускать выходной файл.
Вторая проблема проявилась из-за особенности C++ - неявного вызова конструкторов локальных переменных
структурного типа в месте их объявления.
Транслятор ost по умолчанию генерирует очистку локальных переменных после их объявления, приводя их
в негодность. Поэтому нужно добавлять к команде трансляции в Си ключ -init noinit
.
Это тоже предмет будущей доработки транслятора.
Обновление: обе проблемы были решены и программу можно запустить с помощью команды:
$ ost run 'Mol.Log("water.xyz")' -infr openbabel -m . \ -cc "g++ -xc++ -I/usr/include/openbabel-2.0" ... "-lopenbabel"Здесь опция "-lopenbabel" отделена от основной команды троеточием и добавляется к команде компилятора после указания исходный кодов на C, что позволяет успешно скомпоновать программу.
Отлично. Что-то про Андроид будет?)
ОтветитьУдалитьЧто-то ещё кроме этого - https://vostok-space.blogspot.com/2018/09/android.html ?
УдалитьПока не планирую