познакомьте класс C с октавой

#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;
}