#c #octave
Вопрос:
Я прошелся по Интернету и не нашел подходящего введения о том, как ввести класс C в Octave. То, что я хочу сделать, это что-то вроде
C :
class Foo
{
public:
Foo(int n) {...}
~Foo() {...}
void Print() {...}
};
Октава:
> foo = Foo(9);
> foo.Print();
Кроме того, я также хотел бы знать о векторе октавы для чтения, матрице в коде C (2-сторонняя интеграция).
Комментарии:
1.octave.org/doc/v4.2.1/Getting-Started-with-Oct_002dFiles.html octave.org/doc/v6.1.0/…
2. @CrisLuengo Вопрос сформулирован неявно. Это «Как ввести класс C в Октаву?»
3. Как я уже сказал, учебник о том, как это сделать, выходит за рамки переполнения стека. Есть разница между ответом на вопрос и написанием учебника по широкой теме. Вы просите нас написать учебник.
4. ну, @CrisLuengo, может быть, ты упустил мою мысль. Я просто прошу вводный пример, если есть, и кто-то настолько любезен, чтобы внести свой вклад, чтобы сэкономить время других. Для этого не нужен учебник. Если бы в будущем я мог привести небольшой пример, я мог бы добавить его сюда.
5. @kstn Я вижу как твои, так и точки зрения Криса. Лично я думаю, что вы оба правы по неправильным причинам, ха-ха. Чтобы уточнить: я думаю, что ваш вопрос исходит из предыдущих предположений о том, как работает / должна работать октава, которые неверны. Приведение примера того, как вы использовали бы такой класс в octave , прояснило бы, каковы эти предположения, и дало бы нам «поверхность атаки» для конкретного ответа, разъяснив, каковы ошибочные предположения. Ниже я попытаюсь дать ответ, который атакует «одну из возможных интерпретаций» того, что, по моему мнению, может быть вашими предположениями и почему это предположение неприменимо.
Ответ №1:
В вашем вопросе есть несколько вещей, которые являются ответными словами, что для меня подразумевает, что у вас есть определенные предположения о том, как работает октава (и, в частности, интерфейс octave-C ), которые неприменимы.
Например, если вы сделаете «Foo(9)», каков результирующий объект в октавных терминах? Является ли это объектом октавы? Конечно, нет; нет никаких причин, по которым класс c был бы идентичен классу octave (и было бы довольно большим предположением предположить, что разработчики octave потрудились сделать объекты octave идентично совместимыми с объектами c — задача настолько монументальная, что ни один другой язык не пытался ее выполнить).
Во-вторых, даже если вам удалось каким-то образом создать представление объекта C в октаве … где он живет в памяти и как работают его функции? C — это скомпилированный статический язык, octave-интерпретируемый динамический язык. Так что, на самом деле, вопрос об «импорте» класса c в octave не имеет такого большого смысла, как вы думаете.
Что вы должны спросить, так это есть ли интерфейс, который позволяет вам работать с классами C в октаве.
И ответ-да. Как бы. Нет интерфейса для классов c «конкретно», но есть интерфейс c в целом.
Таким образом, в зависимости от того, как вы хотите использовать объект класса c в octave, вы можете использовать интерфейс octave-c для создания подходящего API для функциональности вашего класса c .
Например, если вам нужен какой-либо «объект» в пределах октавы, у которого есть методы, которые можно вызывать, очевидным выбором было бы создать класс октавы и просто убедиться, что его методы делегированы в файлы .oct/.mex (которые являются скомпилированным кодом c ).
Если вы будете придерживаться т. н. «старому стилю» объектно-ориентированного программирования в октаву (что я предпочитаю), то это так же просто, как замена файлов внутри «@» папки с .файлы октября (ок, в теории, вы можете сделать это со стилем нового classdef’, если вы хотите :Р).
Это также означало бы, что, поскольку методы octave неявно передают объект octave вместе с ними, вашему интерфейсу C придется воссоздать эквивалентный объект C в коде c , а ЗАТЕМ выполнить соответствующий вызов метода и вернуть результат (который может быть новым объектом c , который вы затем преобразуете в объект octave и возвращаете его, чтобы представить измененное состояние).
Альтернативно, если все, что вам нужно, — это использовать некоторую функцию класса c «под колпаком», находясь в среде c , но с точки зрения октавы, все, что вас действительно волнует, — это передача некоторых параметров «инициализации» и получение результата, тогда вам вообще не нужен объект octave, и все, что вам нужно, — это простая функция на основе .oct, которая принимает некоторые параметры (инициализации), передает их в интерфейс c и просто «случается» использовать классы c под колпаком, прежде чем возвращать значение в рабочее пространство octave.
пс. Я не говорил о добавлении «векторного интерфейса» в вашем вопросе, потому что это достаточно подробно описано в документации, как упоминал Крис. Видишь https://octave.org/doc/v6.3.0/Matrices-and-Arrays-in-Oct_002dFiles.html#Matrices-and-Arrays-in-Oct_002dFiles в частности, если это вас интересует. Полный API C доступен по адресу: https://octave.org/doxygen/stable/
Комментарии:
1. Неплохо. Ты гораздо более терпеливый человек, чем я. 🙂
2. Есть и другая возможность, гораздо более сложная. Вам потребуется, чтобы все вызовы методов передавались через один (частный) файл mex/oct. Этот следующий файл/oct будет содержать статический список созданных объектов C , объект Octave будет содержать ссылку на объект C (например, индекс списка). Я предполагаю, что это полезно только тогда, когда объекту C принадлежат ресурсы (открытый файл, окно на экране, данные, которые трудно воспроизвести и т. Д.).
3. «Ты гораздо более терпеливый человек, чем я».: Я верю, что ты ищешь слово «прокрастинатор», Крис! Ха-ха-ха.
Ответ №2:
Вот базовая реализация:
В C вам нужно определить класс-оболочку с именем octave_Foo
, который содержит std::shared_ptr
Foo
объект a и перенаправляет на него вызовы. Он должен быть подклассом octave_base_value
и должен иметь конструктор, который не принимает аргументов. Также у него должна быть функция-член с именем is_defined
. Все остальные функции-члены используются для перенаправления вызовов. В конце определения класса используйте два макроса DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA
и DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA
. После этого используйте DEFUN_DLD
макрос для определения oct
функции , здесь названной Foo_dispatch
, которая получает имя метода в виде строки и других аргументов и вызывает соответствующий метод octave_Foo
класса. Сохраните файл C как Foo_dispatch.cc
и используйте mkoctfile Foo_dispatch.cc
для его компиляции как Foo_dispatch.oct
.
В Октаве вам нужно определить класс Foo
, который является подклассом handle
. Его методы вызываются Foo_dispatch
с соответствующими аргументами.
Обратите внимание, что Foo
это подкласс handle
, потому что класс C octave_Foo
имеет ссылочную семантику вместо семантики значений. Реализация octave_Foo
класса значений as более сложна, поэтому необходимо заботиться о количестве копий при записи и ссылках. Другим вариантом реализации семантики значений является (де)сериализация объекта C в виде типов данных октавы, таких как массивы, что имеет свои собственные затраты.
Существует также название проекта SWIG, которое соединяет программы, написанные на C и C , с различными языками программирования высокого уровня, но я не уверен, работает ли оно с последними версиями Octave.
Октавная часть:
# Foo.m
classdef Foo < handle
properties
m_Foo
endproperties
methods
function obj = Foo (val)
obj.m_Foo = Foo_dispatch ("constructor", val);
endfunction
function Print (obj)
Foo_dispatch("Print", obj.m_Foo);
endfunction
endmethods
endclassdef
Часть C :
// Foo_dispatch.cc
#include <memory>
#include <octave/oct.h>
class Foo
{
public:
Foo(int n) {/*...*/}
~Foo() {/*...*/}
void Print() {/*...*/}
};
class octave_Foo : public octave_base_value
{
public:
octave_Foo (): m_data (new Foo (0))
{}
bool is_defined (void) const
{ return true; }
octave_Foo (int n): m_data (new Foo (n))
{}
void Print () { m_data->Print (); }
private:
std::shared_ptr<Foo> m_data;
DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA
};
DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_Foo,
"Foo wrapper",
"octave_Foo");
DEFUN_DLD (Foo_dispatch, args, nargout,
"Foo_dispatch")
{
octave_value_list retval;
std::string method_name = args (0).string_value ();
if (method_name == "constructor")
{
int val = args (1).int_value ();
retval.append (octave_value (new octave_Foo (val)));
}
else if (method_name == "Print")
{
octave_base_value *object = args (1).internal_rep ();
octave_Foo *foo = dynamic_cast <octave_Foo *> (object);
if (! foo)
error ("expected octave_Foo object!");
foo->Print ();
}
return retval;
}