2

I went through the web and did not find a proper introduction on how to introduce a C++ class to Octave. What I want to do is something like

C++:

class Foo
{
public:
    Foo(int n) {...}
    ~Foo() {...}
    void Print() {...}
};

Octave:

> foo = Foo(9);
> foo.Print();

In addition, I also would like to know about read-in Octave vector, matrix in C++ code (2-way integration).

5
  • 1
  • @CrisLuengo The question is formulated implicitly. That is "How to introduce a C++ class to Octave?"
    – kstn
    Commented Aug 2, 2021 at 16:49
  • As I said, a tutorial on how to do this is out of scope for Stack Overflow. There’s a difference between answering a question and writing a text book on a broad subject. You are asking us to write a text book. Commented Aug 2, 2021 at 18:40
  • well, @CrisLuengo maybe you miss my point. I just ask for an introductory example, if there is and someone is so kind enough to contribute to save other's time. No need a textbook for that. If, in the future, I could make a small example, I could add it here though.
    – kstn
    Commented Aug 2, 2021 at 18:45
  • @kstn I see both yours and Cris' points. Personally I think you're both right for the wrong reasons, hahah. To elaborate: I think your question comes from prior assumptions of how octave works / should work, that are not correct. Providing an example of how you would use such a class in octave would clarify what those assumptions are, and give us an "attack surface" to answer specifically, by clarifying what the mistaken assumptions are. I will attempt an answer below that attacks "one possible interpretation" of what I think your assumptions may be and why this assumption does not apply. Commented Aug 6, 2021 at 10:35

2 Answers 2

1

There's a few things that are backwords in your question, which to me implies you have certain assumptions on how octave (and specifically the octave-C++ interface) works, which do not apply.

E.g., if you do "Foo(9)", what is the resulting object in octave terms? Is it an octave object? Of course not; there's no reason a c++ class would be identical to an octave class (and it would be a rather big assumption to assume that octave devs have gone to the trouble to make octave objects identically compatible with c++ objects -- a task so monumental that no other language has attempted it).

Secondly, even if you did manage to somehow create a representation of a C++ object in octave ... where does it live in memory, and how do its functions operate? C++ is a compiled, static language, octave is an interpreted, dynamic language. So, really, the question of "importing" a c++ class into octave does not make as much sense as you think it does.

What you should be asking, is if there is an interface that allows you to do things with C++ classes, in octave.

And the answer is yes. Sort of. There is no interface for c++ classes "specifically", but there is a c++ interface in general.

So, depending on how you'd like to use the c++ class object in octave, you can use the octave-c++ interface to create a suitable API for your c++ class's functionality.

For example, if you need to have an "object" of some sort, within octave, that has methods that can be called, then an obvious choice would be to create an octave class, and simply make sure its methods are delegated to .oct/.mex files (which are compiled c++ code).

If you stick to the so-called "old style" of object oriented programming in octave (which I actually prefer), then this is as simple as replacing your function files inside the "@" folder with .oct files (ok, in theory, you can do this with the new 'classdef' style too if you want :p).

It would also imply that, since octave methods implicitly pass the octave object with them, your C++ interface would have to recreate the C++ equivalent object in the c++ code, and THEN perform the appropriate method call and return the result (which could be a new c++ object, which you'd then convert to an octave object, and return that, in order to represent changed state).

Alternatively, if all you need is to use some c++ class function "under the hood", while inside the c++ environment, but from an octave point of view all you really care about is passing some "initialization" parameters, and getting back a result, then you don't need an octave object at all, and all you need is a simple .oct based function, which takes some (initialization) parameters, passes them to the c++ interface, and simply "happens" to use c++ classes under the hood before returning a value to your octave workspace.


PS. I didn't talk about the "vector interface" addendum in your question, because that is covered at reasonable depth in the documentation, as mentioned by Cris. See https://octave.org/doc/v6.3.0/Matrices-and-Arrays-in-Oct_002dFiles.html#Matrices-and-Arrays-in-Oct_002dFiles in particular, if interested. The full C++ API is available at: https://octave.org/doxygen/stable/

3
  • Nice. You’re a much more patient man than I am. :) Commented Aug 6, 2021 at 13:26
  • 1
    There’s another possibility, that much more complex. You’d need all method calls to be funneled through a single (private) mex/oct file. This next/oct file would contain a static list of C++ objects created, the Octave object would contain a reference to the C++ object (e.g. the list index). I guess this is useful only when the C++ object owns resources (an open file, a window on a screen, data that is hard to reproduce, etc). Commented Aug 6, 2021 at 13:30
  • "You’re a much more patient man than I am": I believe the word you're looking for is "procrastinator", Cris! Hahahah. Commented Aug 6, 2021 at 14:29
1

Here is a basic implementation:

In C++ you need to define a wrapper class, here is named octave_Foo, that contains a std::shared_ptr to a Foo object and redirects calls to it. It should be subclass of octave_base_value and should have a constructor that takes no arguments. Also it should have a member function named is_defined. All other member functions are used to redirect calls. At the end of the class definition use two macros DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA and DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA. After that use DEFUN_DLD macro to define an oct function , here is named Foo_dispatch, that gets a method name as string and other arguments and calls the proper method of octave_Foo class. Save the C++ file as Foo_dispatch.cc and use mkoctfile Foo_dispatch.cc to compile it as Foo_dispatch.oct.

In Octave you need to define a class Foo that is subclass of handle. Its methods call Foo_dispatch with the proper arguments.

Note that Foo is subclass of handle because the C++ class octave_Foo has reference semantics instead of value semantics. Implementation of octave_Foo as value class is more complicated that needs to care about copy-on-write and reference count. Another option to implement value semantics is (de)serializing the C++ object as Octave data types such as arrays that has its own cost.

There is also a project name SWIG that connects programs written in C and C++ with a variety of high-level programming languages but I'm not sure if it works with the recent versions of Octave.

Octave Part:

# 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++ part:

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.