Oct files: How to get read-only pointers to data for arguments of unknown type?

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Oct files: How to get read-only pointers to data for arguments of unknown type?

Stefan
Hello,

I am trying to write an octave .oct wrapper to a C library that accepts any number of inputs of any type. The library uses a simple descriptor structure, which contains a const void* to the data. Simple and powerful. Writing the wrapper consists of assembling the descriptor structure from the octave_value_list "args", including the const void* to the data. Since the library will not modify the data, which can be huge, there should be no copying.

I have wrapped this library before in Matlab mex, where void *mxGetData(const mxArray *) simply does the job.

There appears to be no such method in the .oct API. Instead, there are specialized conversion constructors for each data type, which are quite aggressively trying to copy the data. So here is my first attempt to attach the const void* to the descriptor "D" without any copying:

if (args(0).is_int32_type()) {
    const int32NDArray tmp = args(0).int32_array_value();
    mkDescrip_data(D, tmp.data());
} else if (...) {
...
} else if (args(0).is_double_type()) {
    const NDArray tmp = args(0).array_value();
    mkDescrip_data(D, tmp.data());
}
call_library(&D);

This doesn't work. Since tmp was defined inside the scope of the switchyard, the array_value() methods have made a copy of the data and returned a pointer to the copy, which got already destroyed before I reach call_library(&D). Great.

If I don't assign to a const NDArray object, array_value() makes a copy in any case.

I have thought about this kind of hack:

// get a const object for all possible data types (this compiles)
const int32NDArray tmp32 = args(0).int32_array_value();
...
const NDArray tmpd = args(0).array_value();

if (args(0).is_int32_type()) {
    mkDescrip_data(D, tmp32.data());
} else if (...) {
...
} else if (args(0).is_double_type()) {
    mkDescrip_data(D, tmpd.data());
}

call_library(&D);

This ugly hack would work if I only had one argument from octave, but I also have args(1), args(2), ... , args(n), all to be assigned before call_library(&D).

I hope that I am missing something in the octave API. Or is there some C++ trick, such as working with the parent class, or deriving a class that adds the needed functionality?

How can one do what mxGetData() does without going completely overboard in the oct API?

Thanks for your ideas,

Stefan

Reply | Threaded
Open this post in threaded view
|

Re: Oct files: How to get read-only pointers to data for arguments of unknown type?

Stefan
Hello,

I figured out how to get a pointer to the data in an octave_value object, regardless of the type, without having octave make a copy:

void *octGetData(const octave_value &in)
{
    if (in.is_complex_type() && !in.is_scalar_type()) {
        // handle complex data types separately, but only if not scalar!
        if (in.is_double_type()) {
            const ComplexNDArray t = in.complex_array_value();
            return (void*) t.data();
        } else if (in.is_single_type()) {
            const FloatComplexNDArray t = in.float_complex_array_value();
            return (void*) t.data();
        } else {
            error("Data type not implemented.");
            return NULL;
        }
    } else {
        // handle bulk of data types with mex_get_data()
        return in.mex_get_data();
    }
}

So there is an octave_value method mex_get_data() that emulates Matlab's mxGetData() function, which handles most cases. For complex data types, mex_get_data() appears to make a copy of the data to emulate Matlab's behavior to return only the real part. This isn't what I wanted, since my library expects complex inputs in octave's storage order. Hence the data-type specific solution for complex data, which - in contrast to my thinking in my previous post - does *NOT* make a copy, UNLESS you happen to have a scalar type (is_scalar_type() is true).

A single complex number was my test case for my previous post, which failed so badly. Fortunately, mex_get_data() works for complex scalars, since the data doesn't have to be rearranged in this case.

I believe the above function does not interfere with octave's reference counting, so you can use it to pass arguments to an oct file by reference:

DEFUN_DLD(oct_test, args, nargout, "Test")
{
    void *data = octGetData(args(0));
    *(Complex*)data = Complex(7.,8.);
    return octave_value();
}

In octave:

octave:1> x = 1+i
x =  1 + 1i
octave:2> oct_test(x)
octave:3> x
x =  7 + 8i

Great, if the data in x fills up half of your computers memory and your point of writing an oct file in the first place was to have access to a fast C library that modifies your data in-place.

Thanks everybody for your time,

Stefan
Reply | Threaded
Open this post in threaded view
|

Re: Oct files: How to get read-only pointers to data for arguments of unknown type?

John W. Eaton
Administrator
On 28-Feb-2012, Stefan wrote:

| I figured out how to get a pointer to the data in an octave_value object,
| regardless of the type, without having octave make a copy:
|
| void *octGetData(const octave_value &in)
| {
|     if (in.is_complex_type() && !in.is_scalar_type()) {
|         // handle complex data types separately, but only if not scalar!
|         if (in.is_double_type()) {
|             const ComplexNDArray t = in.complex_array_value();
|             return (void*) t.data();
| } else if (in.is_single_type()) {
|             const FloatComplexNDArray t = in.float_complex_array_value();
|             return (void*) t.data();
|         } else {
|             error("Data type not implemented.");
|             return NULL;
| }
|     } else {
|         // handle bulk of data types with mex_get_data()
|         return in.mex_get_data();
|     }
| }
|
| So there is an octave_value method mex_get_data() that emulates Matlab's
| mxGetData() function, which handles most cases. For complex data types,
| mex_get_data() appears to make a copy of the data to emulate Matlab's
| behavior to return only the real part. This isn't what I wanted, since my
| library expects complex inputs in octave's storage order.

It has to make a copy because Octave stores complex values in a single
array with alternating real and imaginary parts but Matlab stores them
as two separate arrays, one for the real part and one for complex.

| I believe the above function does not interfere with octave's reference
| counting, so you can use it to pass arguments to an oct file by reference:
|
| DEFUN_DLD(oct_test, args, nargout, "Test")
| {
|     void *data = octGetData(args(0));
|     *(Complex*)data = Complex(7.,8.);
|     return octave_value();
| }
|
| In octave:
|
| octave:1> x = 1+i
| x =  1 + 1i
| octave:2> oct_test(x)
| octave:3> x
| x =  7 + 8i
|
| Great, if the data in x fills up half of your computers memory and your
| point of writing an oct file in the first place was to have access to a fast
| C library that modifies your data in-place.

You are casting away const here, so you may see some unexpected
results.

For example, try

  x = y = 1+i;
  oct_test (x)

and I expect both x and y will change.  This could cause a lot of
trouble for users of your code if they expect the semantics of
function calls in Octave to be consistent and NOT modify inputs to
functions.

jwe
_______________________________________________
Help-octave mailing list
[hidden email]
https://mailman.cae.wisc.edu/listinfo/help-octave
Reply | Threaded
Open this post in threaded view
|

Re: Oct files: How to get read-only pointers to data for arguments of unknown type?

Jordi Gutiérrez Hermoso-2
In reply to this post by Stefan
On 28 February 2012 08:52, Stefan <[hidden email]> wrote:
> I figured out how to get a pointer to the data in an octave_value object,
> regardless of the type, without having octave make a copy:
[code snipped]
> So there is an octave_value method mex_get_data() that emulates Matlab's
> mxGetData() function,

There is also mxGetData in Octave which emulates Matlab's mxGetData:

    http://hg.savannah.gnu.org/hgweb/octave/file/933101fd5cbb/src/mexproto.h#l194

The difficulties you seem to have solved with the Octave API are about
figuring out the type of the octave_value you have received in your
DLD function.

I'm not sure if you'd be happier just using Octave's mex interface if
you already have mex files written for Matlab.

- Jordi G. H.
_______________________________________________
Help-octave mailing list
[hidden email]
https://mailman.cae.wisc.edu/listinfo/help-octave
Reply | Threaded
Open this post in threaded view
|

Re: Oct files: How to get read-only pointers to data for arguments of unknown type?

Stefan
In reply to this post by John W. Eaton
Hello,

Thanks for your help.

| You are casting away const here, so you may see some unexpected
| results.
|
| For example, try
|
| x = y = 1+i;
| oct_test (x)
|

The data behind both x and y will be modified by oct_test(), since x is so far used only as a pointer to y. I don't find this unexpected. Or this:

x = y = 1+i
y = 1+2*i
oct_test(x)

The data in y will not be modified, since y points to a different object now. Just what one expects in a "lazy copy" language.

My applications are usually memory critical. Consider the memory needs of:

my_huge_matrix = octfile(my_huge_matrix)

Here, octave makes a copy of my_huge_matrix at the call of octfile, and then copies the result back to the workspace. So 3 copies of my_huge_matrix must fit into memory. It is particularly annoying if the memory error comes only after the expensive calculation is completed. In pass-by-reference, once I succeed to fit the initial matrix into memory, I know that the result will fit as well.

This is of course the same problem in matlab. Only, since matlab mex is in C, it was easier to get pass-by-reference behavior for mex files. In octave's C++ API, everything is overloaded and has side effects, so you never know which seemingly harmless statement causes a copy of the data behind the scenes. (On a side note, I always thought that a simple void* in C is vastly superior to C++ templates. Why all the copy-paste source files like int32NDArray.cc, int64NDArray.cc, dNDArray.cc, ... ?)

There could be a mechanism to attach an attribute to a variable that should be passed by reference, like IDL's TEMPORARY mechanism. Something like this might be a good idea.

Thanks for the discussion,

Stefan