LINUX.ORG.RU

Можно ли работать с шаблонами С++ в питоне без инстацирования?

 , ,


0

5

Есть некий библиотечный класс

template<int D, typename T=double> Vec{
 T p[D];
public:
 ...
};

// И попробуйте протащить это в SWIG!
template <int D, typename T1, typename T2> 
inline decltype(T1()*T2()) operator * (const Vec<D, T1> &a, const Vec<D, T2> &b){...}
и использующая его функция
void foo(Vec<3>& v);

Что бы протащить все это добро в питон через swig надо указать в .i-файле

%template(Vec3) Vec<3,double>;
попатчить питоновскую оболочку (фактически написать руками с нуля те методы которые не прошли в SWIG автоматом) и все работает.

Теперь вопрос - а как это сделать НЕ указывая директиву %template (и не компилируя кусок кода связанный с инстацированием Vec<3> В ПИТОН, понятно что инстацирование в плюсах есть)? Желательно не поломать систему контроля типов swig. Отвечая на вопрос ЗАЧЕМ - таких инстацирований иногда бывает очень много (с разными аргументами шаблонов), иногда их приходится делать на лету (вылезло че то новое в питоньем рантайме). Это работает, но получается громоздко и создает ряд проблем.

Я могу на питоне написать класс, полностью реализующий работу Vec, могу даже сформировать из питона в памяти объект в точности отвечающий нужному плюсовому Vec. Вопроc как его дальше пропихнуть через swig в плюсы сохранив систему контроля типов от swig-а...

Я знаю, что swig руководствуется полем this - оно должно указаывать на плюсовый объект в паямти, с т.з. свига имеющий правильный тип. А как свиг понимает что тип правильный?

И как понять, что некая плюсовая переменная имеет параметризованный тип? У swig-а есть таблица типов, куда попадают в т.ч. и неинстацированные шаблоны. С ней можно как то работать из питона (ну хотя бы читать)?

------------------------------------------------------------

UPD: У свига есть таблица swig_types. Поле this питоньего объекта указывает на экземпляр

typedef struct {
  PyObject_HEAD
  void *ptr;
  swig_type_info *ty;
  int own;
  PyObject *next;
#ifdef SWIGPYTHON_BUILTIN
  PyObject *dict;
#endif
} SwigPyObject;
в котором ptr указывает на плюсовый объект, а ty задает тип (из таблибцы swig_types). Т.е. можно таки ручками собрать объект любого известного свигу типа, что порождает по крайней мере такие вопросы:

1) не хочется в питоне поддерживать поле this в актуальном виде все время, хочется его генерить только при передаче объекта в свиг. Как это сделать (перегрузка __gettattr__ не помогает)?

2) Как понять к какому типу свиг кастует объект (аргумент фунцкции) что бы находу генерить правильный this?

3) Для не-обернутых объектов свиг верещит что нет деструктора.

4) Не слишклм ли это все гнусно выглядит... ?;-)

Сast tailgunner, true_admin, monk - кто там у нас еще был Гуру по всяким извращениям;-)

★★★★★

Это работает, но получается громоздко и создает ряд проблем.

«Одному богу известно что под этим скрывается» (c) Фе.Фе.Преображенский.

anonymous ()

Никак! В плюсах статическая типизация! До тех пор, пока шаблон явно не используется в коде он остаётся абстракцией для компилятора. Python через SWIG использует явно скомпилированный код => для появления этого бинарного кода и пишется %template....

Так что меняй логику, так что бы вообще таких шаблонов из вне не было видно.

З.Ы. Через SWIG нормально можно работать и со стандартным std::vector<double>.

AlexVR ★★★★★ ()

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

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

Лор такой Лор...

В плюсах статическая типизация!

Спасибо, Кэп!

Никак!

Вы таки ошибаетесь. Мне ничего не мешает сформировать в питоне объект приведенного выше шаблона с любыми разумными параметрами при помощи моудля struct. И я даже могу просунуть его в плюсы (пококвырялся сегодня во .._wrap.cxx - это возможно). Но это уж больно смахивает на хак, реализация довольно заморочная и имеет ряд проблем которые я пока не понимаю как решить. Меня с-но интересует, есть ли способы делать такие вещи штатно и прямо?

Через SWIG нормально можно работать и со стандартным std::vector<double>.

Спасибо еще раз, дважды Кэп! Лор такой Лор...

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

Всё-таки нельзя, потому что если не было инстанциирования, то компилятор ничего не скомпилирует

И я даже могу просунуть его в плюсы (пококвырялся сегодня во .._wrap.cxx - это возможно)

Код в студию

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

Ок, я не вполне корректно выразился.

Есть инстацирование в плюсах, а есть инстацирование в питон через свиговый %template. Я имел ввиду второе.

Понятно же, что речь идет о работе в питоне с плюсовыми шаблонами. Я без проблем могу юзать аналог парметризованного класса (по поведению и возможностям) в питоне, и даже делать struct-ом его данные для плюсов - вопрос в том, как его передать в плюсы через свиг. И этот вопрос имеет смысл ТОЛЬКО если класс был инстацирован в плюсах, но не был инстацирован в питон через свиг.

Код в студию

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

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

Мне ничего не мешает сформировать в питоне объект приведенного выше шаблона с любыми разумными параметрами при помощи моудля struct.

Кто пустил этого байтосексуалиста в высокоуровневые ЯП?

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

И этот вопрос имеет смысл ТОЛЬКО если класс был инстацирован в плюсах, но не был инстацирован в питон через свиг.

Тогда с точки зрения плюсов претензий не имею. С питоном не работал, но разве нельзя реверс-сгенерировать свиговский файл по скомпилированному плюсовому (по экспортам например)?

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

Есть инстацирование в плюсах, а есть инстацирование в питон через свиговый %template.

это одно и то же, в cpp добавляется explicit template specialization, к нему враппер, ЗАТЕМ компиляция, к питону это не имеет никакого отношения

struct это сцаные байтики, к которым методов не завезли, т.к. машинный код специализации в большинстве случаев не универсален.

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

С питоном не работал, но разве нельзя реверс-сгенерировать свиговский файл по скомпилированному плюсовому (по экспортам например)?

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

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

Нет, это НЕ одно и тоже. Инстацирование в питон безусловно требует инстацирование в плюсы, но инстацирование в плюсы (и работа с интстацированными в плюсы классами в питоне) не требует обязательного инстацирования в питон. В частности не обязательно компилить врапперы и прочее садо-мазо.

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

Что делает свиг и зачем ему нужен %template я довольно таки хорошо знаю. А вот зачем Вы тут пишете, раз ничего не в состоянии сказать по делу, я не пойму.

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

Спасибо еще раз, дважды Кэп! Лор такой Лор...

Ты задаёшь вопросы аккурат для Капитана Очевидность, а потом возмущаешься очевидным ответам? Чего ждал-то вообще?

Очевидно же, что в C++ не существует шаблонов во время исполнения, они уточняются во время компиляции. То есть ответ на твой вопрос — нет. И ты это прекрасно знаешь.

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

Беда в том, что Рудалев очень по лоровски не понял вопроса и кинулся отвечать не на мой вопрос а на что то свое, простое и мне не интерсеное. Даже странно что мы с ним, похоже, коллеги...

Если Вы потрудитесь прочитать ветку, то увидите что ответ на мой вопрос таки ДА. Меня с-но интересует можно ли это сделать бол мен прямо/стандартными свиговыми методами, как это сделать криво я знаю.

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

что ответ на мой вопрос таки ДА

Это ответ на другой вопрос. Вопрос-то был:

можно ли это сделать бол мен прямо/стандартными свиговыми методами

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

нет, исходный вопрос был

как это сделать НЕ указывая директиву %template

Как это сделать стандартно и прямо - это уже второй вопрос. Но тут нужен специалист по swig-у а не ЛОР-гуру-по-всему.

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

Ну если ДА, то плиз пример того на чём застрял, а то до меня не доходит каким это макаром у тебя получается ДА. Видимо мало я использовал SWIG и ничего в нём не понимаю.

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

Смотрим во wrap.cxx раздел TYPES TABLE (массив swig_types). Размер таблицы можно посмотреть в cтруктуре swig_module_info (поле size). Т.е. мы можем считать таблицу поддерживаемых свигом в конкретном модуле типов, включая те которые инстацированы в плюсы но не обернуты в питон - именно они нас интересуют.

С т.з. свига со стороны питона тип плюсового объекта задается полем this, которое содержит объект типа SwigPyObject.

typedef struct {
  PyObject_HEAD
  void *ptr;
  swig_type_info *ty;
  int own;
  PyObject *next;
#ifdef SWIGPYTHON_BUILTIN
  PyObject *dict;
#endif
} SwigPyObject;
нам интерсны поля ptr (с-но плюсовый объект) и ty (тип объекта с т.з. свига, что то из swig_types). Это дает возможность сделать для свига из питона (при помощи чисто питоньего кода и вызова нескольких простых плюсовых ф-й) объект любого типа зарегистрированного в swig_types.

Пока (кроме общего ощущения что это ugly hack и всяких мелочей типа возможной утечки памяти если руки будут кривые, но %template на каждый чих еще хуже) есть три конкретные проблемы:

1) я не хочу все время в питоне держать актуальным поле this, мне бы хотелось генерить его по необходимости автоматом когда питоний объект уходит свигу. Пока я не могу это отловить, перегрузка __getattr__ не помогает, хотя вроже как должна.

2) я хочу знать к какому типу свиг пытается скастовать полученный объект, что бы самому дальше генеить соответсвующий this если возможно.

3) для не-обернутых объектов свиг верещит что не может найти деструктор при выходе.

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

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

Это библиотека. Новая версия имеет проапгрейченый набор операций, параметризованных при помощи С++11 по типам обебих аргументов в частности. Свиг это один фиг автоматом не съест, и приходится либо патчить его питоний выхлоп от %template, либо независимо писать - второе проще. Но проблема с передачей данных назад в плюсы.

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

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

А смысл? Зачем писать код дважды, при этом тот же деструктор нужный SWIG так и не появиться.

Это библиотека.....

http://www.swig.org/Doc3.0/SWIGPlus.html#SWIGPlus_nn2

Тут чётко пытаются донести, что SWIG предназначен для предоставления доступа к БИНАРНОМУ коду, а использование шаблонов нужно минимизировать, не забывая превращать их в конкретный БИНАРНЫЙ код. Иначе "... and provides many opportunities to shoot yourself in the foot ..."

Но проблема с передачей данных назад в плюсы.

Во что? В шаблонную функцию? Так её бинарного кода нет и передавать данные нечему. В функцию со специализированным шаблоном? Так тогда надо было перед её импортом в SWIG определить %template.

и приходится ... патчить его питоний выхлоп от %template,

Это ещё раз подтверждает, что нужен нормальный внешний API для библиотеки. А что уже будет с внешней стороны библиотеки не для C++ языков: наследование, PIMPL, фабрики, швабрики, ... Это уже совсем другой вопрос.

В любом случае. Что мешает написать на плюсах обёртку над твоей библиотекой, которую легко переварит SWIG?

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

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

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

Во что? В шаблонную функцию? Так её бинарного кода нет и передавать данные нечему. В функцию со специализированным шаблоном?

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

void foo(Vec<3>& v);
стала шаблонной? И такая функция НЕ требует определения в свиге через %template. Нужно лишь каким то образом создать экзмепляр класса Vec<3>, причем он может быть не определен через %template а являться результатом вызова ф-ии
Vec<3> bar();
например. Или быть собран руками по описанной выше технологии.

AIv ★★★★★ ()

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

Желание написать супер-пупер код с кучей хитрожопых темплейтов быстро улетучевается когда приходится поддерживать чужой код. Особенный трэш и угар наступает когда по каким-то причинам это всё перестаёт работать на новых версиях библиотек/компиляторов/софта.

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

Поэтому... по треду ничего не скажу, но буду продолжать читать.

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

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

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

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

Я выбираю не самое «продвинутое» решение проблемы, а то которое легче поддерживать и в котором я смогу разобраться, скажем, через год.

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

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

Но без %template(Vec3) Vec<3,double>; вызов foo(bar()) в Python приведёт к

swig/python detected a memory leak of type 'Vec< 3,double > *', no destructor found.

Так как SWIG-у никто не сказал, где он находится. И информация о Vec< 3,double > является не полной.

Видя же %template(Vec3) Vec<3,double>; SWIG дополнит информацию об этом типе, зарегистрировав методы (в том числе конструктор и деструктор). Сравни *_wrap.cxx с и без %template. Если уж ты так хочешь и дальше извращаться, то смотри эту разницу кода.

И пока это верно даже для extern template, что странно с моей стороны.

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

Да, но думаю что это лечится (и визги свига, и утечка).

Еще раз - я знаю что делает свиг. Я с ним 10 лет плотно ковыряюсь.

AIv ★★★★★ ()

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

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

Ну на лету - это и есть автоматически же:-)

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

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

Если тебя устраивает import hook, тупо генерируй все инстанциации и вызывай SWIG оттуда. За это ты сгоришь в аду, но это будет потом %)

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

Все не получится - собираться будет долго. Я сейчас хочу хакнуть свиговую систему типов и вообще ничего не инстацировать:-)

AIv ★★★★★ ()

Правильный ответ тут уже написали, но если очень очень нужно инстанциировать шаблоны в рантайме и никак иначе не написать - можно компилировать C++ код в рантайме через LLVM. Вот ключевые слова (проекты): cling, cxx.jl, jyt.io

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

Простите, но Вы неправильно поняли вопрос;-(

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

Ну я вроде понял как. В плюсах понадобится лазить по памяти объекта:

void push_data(void *obj, int offset, const char* data, int size){
	for(int i=0; i<size; i++) ((char*)obj)[offset+i] = data[i];
}
PyObject* pull_data(void *obj, int offset, int size){
	return PyString_FromStringAndSize( ((const char*)obj)+offset, size);
}
для данной задачи можно наверное как то заюзать carray.i, но пока лень.

Читать и патчить свиговую таблицу типов

int types_table_size(){ return swig_module.size; }
const char* types_table_get_item(int i){ return swig_types[i]->str; }
void types_table_patch(int src, int dst){ swig_types[i]->clientdata = swig_types[j]->clientdata; }

Получать и задавать свиговый тип объекта

const char* get_swig_type(PyObject* obj){ return ((SwigPyObject*)obj)->ty->str; }
void set_swig_type(PyObject* obj, int T){ ((SwigPyObject*)obj)->ty = swig_types[T]; }

Ну и сам универсальный Vec:

class UVec{ char p[1024]; };
размер такой что бы влез любой разумный Vec.

Остальное делается в питоне.

1) Патчим питонью оболчку UVec --- добавляем в нее все необходимые методы, это в любом случае делать придется.

2) При загрузке модуля читаем таблицу типов, находим в ней UVec. Находим все Vec<...> - это то что может понадобиться передавать из питона, они инстаицрованы в плюсах но не имеют питоньих оболочек.

3) Меняем при помощи types_table_patch(...) для всех Vec поле clientdata на UVec. Это поле задает питонью оболочку, которая генерится при создании, т.е. как только мы пытаемся получить объект Vec<...> в питоне, например при вызове

Vec<3> foo();
мы получим питонью оболочку UVec, у которой this будет ссылаться на SwigPyObject который будет ссылаться на честный Vec<3>. Это же решает проблему деструктора.

Дальше с этим объектом можно работать питоньими методами UVec, которые могут при помощи get_swig_type узнать на какой именно объект UVec ссылается (и какие там данные).

Если мы из питона с нуля создаем UVec какого то типа который уже есть в таблице, то мы ему (в конструкторе) при помощи set_swig_type устанавливаем этот тип, и вуаля - можем его пихать дальше в

void bar(Vec<3, double>&);

Как сваяю бол-мен рабочий пример наверное выложу тут и закрою тему. В простых тестах все получается пока...

AIv ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.