bytedeco / javacpp

The missing bridge between Java and native C++
Other
4.47k stars 582 forks source link

How to map typdef and struct #29

Closed phanthanhliem closed 9 years ago

phanthanhliem commented 9 years ago

Hi, I have a struct like the one below:

typedef std::vector<double*>        Vector_p_Double;
struct structTimeSeriesResults
{   
    Vector_p_Double         vpd_result;
    double*                 p_time;
    int                     nbPoints;
};

I've tried to use std:vector<double*> directly, it works. For example: std:vector<double*> vpd_result; But the problem appears when I use:

typedef std::vector<double*>        Vector_p_Double;

I've read this thread, which has the similar problem: https://groups.google.com/forum/#!searchin/javacpp-project/typedef/javacpp-project/YhOOggLfkzc/0i7kp3YqrFoJ

but I really don't understand the code.

How could you map this into javacpp?

Thanks for your help

saudet commented 9 years ago

There's a discussion about that at issue https://github.com/bytedeco/javacpp/issues/22. Let me know if that doesn't answer the question.

saudet commented 9 years ago

Ah same guy, so I guess it doesn't answer your question. So, what is the question? Could you provide more details about what steps you are taking, what errors you are getting, etc? Thanks

phanthanhliem commented 9 years ago

Hi Saudet,

Sorry for my incomprehensible question. The problem is when I used typedef like this:

typedef std::vector<double*>        Vector_p_Double;
struct structTimeSeriesResults
{   
    Vector_p_Double         vpd_result;
...
}

There is an error when I compile: image which tells that javacpp cannot convert from Vector_p_Double to std::vector<double *,std::allocator<_Ty>> *. It seems that javacpp does not understand the new type Vector_p_Double defined by the typedef command.

Without using typedef std::vector<double*> Vector_p_Double;, or more exactly using directly std::vector<double*> it works fine like in https://github.com/bytedeco/javacpp/issues/22

So the question is how could I use typedef std::vector<double*> Vector_p_Double;? Hope that you understand my question now.

Thank you for your help. Thanh Liem.

saudet commented 9 years ago

Ok... Would you have a complete self-contained example that I could try to run here? Thanks!

phanthanhliem commented 9 years ago

Hi Samuel, I've send a self contained example to your email. Thanks for your help.

saudet commented 9 years ago

I mean some small code example, of less than 100 lines...

phanthanhliem commented 9 years ago

Hi Samuel

This is the header spider.h

#ifndef SPIDER_H
#define SPIDER_H

#ifdef SPIDER_CPP_LIBRARY
#define SPIDERDLL_API __declspec(dllexport)
#else
#define SPIDERDLL_API __declspec(dllimport)
#endif

namespace Spider_CPP {
class spider {
public:
    SPIDERDLL_API spider();
    SPIDERDLL_API Struct_Simulation_Results * runSingleSimulation(std::string Simulator_Parameters_Full_File_Name);
}
}

typedef std::vector<double>         Vector_Double;
struct Struct_Simulation_Results
{
    Simulation_Flag         Flag;
    structTimeSeriesResults * s_timeSeriesResults;
    Vector_Double           vd_Indicators;    
};

enum Simulation_Flag
{
    Simulation_OK = 0,
    Initialisation_Failed = 1,
    Simulation_Failed = 2
};

typedef std::vector<double*>        Vector_p_Double;
struct structTimeSeriesResults
{
    Vector_p_Double  *     vpd_result;    
    double*                 p_time;
    int                     nbPoints;
};

And the javacpp source code, testspiderdll.cpp

@Platform(include={"spider.h"}, link="SPIDER")

@Namespace("Spider_CPP")
public class testspidercpp {
    public static class spider extends Pointer {
        static { 
            System.loadLibrary("jnitestspidercpp");             
        }
        public spider() { allocate(); }     
        private native void allocate();
        public native Struct_Simulation_Results runSingleSimulation(@StdString String Simulator_Parameters_Full_File_Name);
    }

    @Namespace("")  
    public static class Struct_Simulation_Results extends Pointer {
        static { Loader.load(); }

        public Struct_Simulation_Results () { allocate(); }     
        private native void allocate();

        public native @Cast("Simulation_Flag") int Flag(); public native Struct_Simulation_Results Flag(@Cast("Simulation_Flag") int Flag); 

        public native structTimeSeriesResults s_timeSeriesResults(); public native Struct_Simulation_Results s_timeSeriesResults(structTimeSeriesResults s_timeSeriesResults);

        public native @StdVector DoublePointer vd_Indicators(); public native Struct_Simulation_Results vd_Indicators(@StdVector DoublePointer vd_Indicators);
    }

    @Namespace("")
    public static enum Simulation_Flag {
        Simulation_OK(0), Initialisation_Failed(1), Simulation_Failed(2);
        private int value;
        private Simulation_Flag(int value) {
            this.value = value;
        }

        public static Simulation_Flag fromInt(int v) {
            return Simulation_Flag.values()[v];
        } 

        public Simulation_Flag getType() {
            return Simulation_Flag.fromInt(nativeGetType());
        }

        private native @Name("getType") int nativeGetType();
    }

    @Namespace("")
    public static class structTimeSeriesResults extends Pointer {
        static { Loader.load(); }
        public structTimeSeriesResults () { allocate(); }       
        private native void allocate();

        public native VectorDoublePointer vpd_result(); public native structTimeSeriesResults vpd_result(VectorDoublePointer vpd_result);
        public native DoublePointer p_time();   public native structTimeSeriesResults p_time(DoublePointer p_time);
        public native int nbPoints();   public native structTimeSeriesResults nbPoints(int nbPoints);       
    }

    @Namespace("")
        // std:::vector<double*> in the line below works fine 
    //@Name("std::vector<double*>") public static class VectorDoublePointer extends Pointer {
    @Name("Vector_p_Double") public static class VectorDoublePointer extends Pointer {
        static { Loader.load(); }

        public VectorDoublePointer(Pointer p) { super(p); }
        public VectorDoublePointer(DoublePointer ... array) { this(array.length); put(array); }
        public VectorDoublePointer()       { allocate();  }
        public VectorDoublePointer(long n) { allocate(n); }
        private native void allocate();
        private native void allocate(@Cast("size_t") long n);
        public native @Name("operator=") @ByRef VectorDoublePointer put(@ByRef VectorDoublePointer x);

        public native long size();
        public native void resize(@Cast("size_t") long n);

        @Index public native @Cast("double*") DoublePointer get(@Cast("size_t") long i);
        public native VectorDoublePointer put(@Cast("size_t") long i, DoublePointer value);

        public VectorDoublePointer put(DoublePointer ... array) {
            if (size() != array.length) { resize(array.length); }
            for (int i = 0; i < array.length; i++) {
                put(i, array[i]);
            }
            return this;
        }
    }   

    public static void main(String[] args) {
        spider dllInstance = new spider();      
        System.out.println("spider.dll is successfully loading!!\n");
        String simulator_param_fullpath = "C:\\Users\\tp242786\\MyPostdoc-INES\\SPIDER\\SPIDER Qt5.4.2_MSVC2013_64bit\\Plant_And_Controls.parameters";
        dllInstance.runSingleSimulation(simulator_param_fullpath);
    }   
}

So how could i write the javacpp code for using:

typedef std::vector<double>         Vector_Double;
typedef std::vector<double*>        Vector_p_Double;

Hope that these source codes is enough for you to reproduce a running example. Thanks for your support.

saudet commented 9 years ago

Vector_Double and Vector_p_Double are declared in the Spider_CPP namespace, so their effective types are Spider_CPP::Vector_Double and Spider_CPP::Vector_p_Double, respectively. They are NOT Vector_Double and Vector_p_Double.

phanthanhliem commented 9 years ago

Hi Samuel,

No, Vector_Double and Vector_p_Double are NOT declared in the Spider_CPP namespace. In fact, Spider_CPP namespace contains just this:

namespace Spider_CPP {
  class spider {
     public:
        SPIDERDLL_API spider();
        SPIDERDLL_API Struct_Simulation_Results * runSingleSimulation(std::string     Simulator_Parameters_Full_File_Name);
  }
}

I put @Namespace("") to escape the Spider_CPP namespace, and this works well for other structs. So do you have an example with the typedef structure?

Thanks and best regards.

phanthanhliem commented 9 years ago

Hi Samuel,

It's ok now, in the spider.h, I need to declare like this

Vector_p_Double       * vpd_result;

instead of

Vector_p_Double       vpd_result;

So, vpd_result must be declared like a pointer, which is compatible with the javacpp source code:

@Name("Vector_p_Double") public static class VectorDoublePointer extends Pointer { ...}

I have one more question, if I don't want to use the pointer * in C++, so I got `Vector_p_Double vpd_result;. How do I define the class VectorDoublePointer in javacpp now?

Thanks for your support.

saudet commented 9 years ago

I see, well I just tried your example here and after fixing a few issues with your C++ code, it works just fine with Vector_p_Double. So, I still don't understand what the issue is. Please provide some sample code so that I can reproduce the issue here. Thank you.

phanthanhliem commented 9 years ago

Hi Samuel,

Thanks for your patient. I will reexplain the problem :

With the definition of VectorDoublePointer in testspidercpp.java,

@Name("Vector_p_Double") public static class VectorDoublePointer extends Pointer { ...}

you could only declare Vector_p_Double * vpd_result; in C++ because VectorDoublePointer extends the class Pointer. Otherwise, a definition without pointer *, for example Vector_p_Double vpd_result; will cause an error in the compilation.

However, the C++ code in my project is developped by some one else, so in this case, I have to insert the pointer notation *, for example, before all the struct defined in the C++ module.

- Struct_Simulation_Results        *       spider::runSingleSimulation(std::string Simulator_Parameters_Full_File_Name)
- Struct_Simulation_Results   *    s_simulationResults
- Vector_p_Double       * vpd_result; 
- ...

So the question is how could I redefine the class VectorDoublePointer or structTimeSeriesResult to remove the usage of the pointer * in the C++ side. Or Which predefined class should I extend instead of Pointer. If this is possible, do you have an example? In javacpp-presets, most classes extend Pointer.

Hope that my question is clearer now. Thanks for your help.

saudet commented 9 years ago

Ah I see. Because it's just a typedef we can simply use the original name std::vector<double*>. They are just synonyms!

phanthanhliem commented 9 years ago

Hi Samuel,

The problem of using Vector_p_Double or std::vector<doubble*> is OK now. In fact, using one of these two definitions is OK, we got the same result:

@Name("Vector_p_Double") public static class VectorDoublePointer extends Pointer { ...}
@Name("std::vector<double*>") public static class VectorDoublePointer extends Pointer { ...}

However in C++ side, I have to declare a pointer (ex: Vector_p_Double * vdp or std::vector<double*> * vdp), because the class VectorDoublePointer extends Pointer class. I can not declare a variable without pointer like this Vector_p_Double vdp or std::vector<double*> vdp.

My problem is: I want to use a var without pointer in C++ side (Vector_p_Double vdp or std::vector<double*> vdp), and how could I change correspondingly the definition of class in javacpp. In this case, the old definition of VectorDoublePointer which extends Pointer doesn't work anymore, there will be always a compilation error.

Hope that it is clearer for you, Samuel.

saudet commented 9 years ago

For something like this:

typedef std::vector<double*>*  Vector_p_Double;
Vector_p_Double vdp();

Of course we can do something like this:

@Name("std::vector<double*>") public static class VectorDoublePointer extends Pointer { ... }
public static native VectorDoublePointer vdp();

That works just fine, no problem. So, your question must be about something else.

phanthanhliem commented 9 years ago

Hi Samuel,

Sorry, :D, I don't know how to explain my prob clearer. I need to use this declaration in C++: typedef std::vector<double*> Vector_p_Double;

But the code in javacpp which I found in the preset @Name("std::vector<double*>") public static class VectorDoublePointer extends Pointer { ... } just support some thing like this: typedef std::vector<double*> * Vector_p_Double;

How could I modify the class definition in javacpp code? For example, by removing extends Pointer, we need to change many things in the body ?

saudet commented 9 years ago

typedef has no effect on Pointer, or vice-versa: the issue is something else...

phanthanhliem commented 9 years ago

Hi Samuel, Yes, the prob is not typedef. But between typedef std::vector<double*> Vector_p_Double; and typedef std::vector<double*> * Vector_p_Double; The difference here is Vector_p_Double vs * Vector_p_Double

The second one is ok as we discussed, but how about the first one? Thanks for your help.

saudet commented 9 years ago

I'm afraid I still don't understand the issue.

Have you tried to use the Parser instead?

phanthanhliem commented 9 years ago

Yes, i've tried to use the Parser. The following sourceds are the content of Stuff.h and Stuff.java (generated from StuffConfig).

Stuff.h
std::vector<unsigned char> foo();
std::vector<double*> vpd_result;
std::vector<double*> * vpd_result_pointer;
Stuff.java
// Parsed from Stuff.h
// #include <vector> 

public static native @Cast("unsigned char*") @StdVector BytePointer foo();
public static native @Cast("double**") @StdVector PointerPointer vpd_result(); public static native void vpd_result(PointerPointer vpd_result);
public static native @Cast("double**") @StdVector PointerPointer vpd_result_pointer(); public static native void vpd_result_pointer(PointerPointer vpd_result_pointer);

I didn't understand why there is no difference between vpd_result & vpd_result_pointer in Stuff.java, although in Stuff.h, they are not the same thing.

saudet commented 9 years ago

The @StdVector adapter just copies the whole vector and returns the copy. It doesn't matter if the original vector is returned by pointer or by value.

phanthanhliem commented 9 years ago

Hi Samuel,

I still couldn't figure out how does it work. I've tried this example this works well.

stuff.h
std::vector<std::string>           vd_Indicators;
stuff.java
public native @StdVector DoublePointer vd_Indicators(); 
public native Struct_Simulation_Results vd_Indicators(@StdVector DoublePointer vd_Indicators);

However if I changed the definition in stuff.h

stuff.h
//std::vector<std::string>           vd_Indicators;
std::vector<std::string>          * vd_Indicators = new std::vector<std::string>();

It causes immediately a crash in the java virtual machine. I assume that in this case the Java definition @StdVector DoublePointer vd_Indicators() could not be used with std::vector<std::string> * vd_Indicators.

So I don't understand your phrase Samuel. A pointer or a value is really a problem here.

phanthanhliem commented 9 years ago

Another example,

stuff.h
std::vector<std::string>  *  vs_labels = new std::vector<std::string>();
stuff.java
@Name("std::vector<std::string>") public static class StringVector extends Pointer { }

Here I retakes the definition in open_cv.core. There no problems with the pointer std::vector<std::string> * vs_labels.

How ever if I use a value instead

stuff.h
//std::vector<std::string>  *  vs_labels = new std::vector<std::string>();
std::vector<std::string>  vs_labels;

then the JVM is crashed as the previous example.

Somes observations here:

saudet commented 9 years ago

You're right that there was some native support missing for vector pointers to work with @StdVector. It should work with the changes from the latest commit: https://github.com/bytedeco/javacpp/commit/f61ac32ec07bb0c1e0e96e40480ebac40823c6bd Let me know! Thanks

phanthanhliem commented 9 years ago

Hi Samuel,

But it's ok now, by placing a @ByVal before StringVector in the class (struct) that use this StringVector, for ex:

public native @ByVal VectorStringPointer vs_labels();
public native structTimeSeriesResults vs_labels(@ByVal VectorStringPointer vs_labels);

I understand JavaCPP better now. Thanks for your help.