Для примера я взял открытую 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 ?
УдалитьПока не планирую