Menu

Wrapping Function Pointers

scepticx
2007-11-19
2013-04-24
  • scepticx

    scepticx - 2007-11-19

    Hi there,

    I was thinking about how to wrap the function Pointers and maybe I found a way but I don't really know if this is the best solution because I'm relativly new to the JNI.

    I have a library that sets a function to calculate the height at a given coordinate via a function pointer.

    void setHeightFunction(Ogre::Real (*heightFunction)(Ogre::Real x, Ogre::Real z)) {}

    So I thought about adding a function in the wrapped C code which is given to this function. eg

    Ogre::Real calcHeight(Ogre::Real x, Ogre::Real z) {}

    And this function should then trigger a Java-Method Using JNI eg

    float calcHeight(float x, float z) {}

    1. Problem: We would have to hardcode the function name and tell the user, to implement this function or maybe use a methodId and Reflection to call the according java-method...

    2. Problem: We would have to initialize the setHeightFunction with the calcHeight-Function to set this function to be called by the original lib

    What do you think, is there a better solution?

    greets

    Ole

     
    • Christoph Nenning

      Well, in any case we need a c++ function which can be called by native code. That function may do the calculation by itself or call a Java method. If a Java method is called then it could be hard coded which one or we could use inheritance to allow user specific functions.

      To allow custom Java methods we need an abstract class which has an abstract method ( calcHeight in your example ). The constructor of this class should register a c++ function which calls the abstract method at the native library (that means the constructor of the Java class has to call a native method). When an user wants to provide that calcHeight method he can just inherit from the abstract class and implement this method.

      When implementing this we must be careful when a JNIEnv pointer is valid. We may need jni references in c++ and may need to free them.

       
      • scepticx

        scepticx - 2007-11-22

        Hi again,

        If I get it right, the solution should look like this:

        Java Code    |    JNIJavaCode    |    JNICCode    |    NativeLib
        (our app)    |   (the *4j.jar)   | (the *.4j.dll) | (the original dll)
        -----------------------------------------------------------------------
                     |                   |                |
                     |                   |   calcHeight  <=  setHeightFunction
                     |                   |       |        |
                     |    calcHeight    <=  jniCalcHeight |
        calcHeight  <=  (default impl.)  |                |
                     |                   |                |

        So we let the setHeightFunction Point to the calcHeight with the declared signature (Ogre::Real getHeight(Ogre::Real x, Ogre::Real z)),
        that calcHeight methods triggers the jniCalcHeight to get the return value,
        the jniCalcHeight calls the calcHeight in our JNI java class (which is a default implementation that returns 0),
        and we can override the default calcHeight with our own if we want to.

        I now read a bit about JNI (but I'm still new to that stuff)
        and I wrote this piece of C code and put it into the *4j.dll:

        /*
        * This calls our default java method that computes the height
        */
        JNIEXPORT jfloat JNICALL Java_org_xbig_TreeLoader2D_getHeight(
          JNIEnv* _jni_env_,
          jobject _jni_this_,
          jfloat x,
          jfloat z
        )
        {
          jclass cls;
          jmethodID mid;

          //get the class that contains the method
          cls = env->GetObjectClass(_jni_this_);

          //get the methods ID
          mid = env->GetMethodID(cls, "getHeight", "(FF)F" );

          //call the method (using CallMethod according to the returnvalue)
          return env->CallFloatMethod(cls, mid, x, z);
        }

        /*
        * A Pointer to this function is given to the native setHeightFunction so that it points to this method
        */
        Ogre::Real getHeight(Ogre::Real x, Ogre::Real z)
        {
          //here we call the Java_org_xbig_TreeLoader2D_getHeight Method
          return Java_org_xbig_TreeLoader2D_getHeight();
        }

        Sorry for errors, it's been a while since my last c-programm... :)
        But do you think this could be a solution?
        Would it be hard to implement this into XBiG?
        I was hoping that we could get this to work soon, so we could use this when wrapping ogre4j...

        Thank you VERY much again, I really appreciate your help and support.

        greets, ole

         
        • scepticx

          scepticx - 2007-11-22

          ups :D

          sry about the table, the spaces have been removed...

           
    • Christoph Nenning

      > sry about the table, the spaces have been removed...
      That is ok, they are in the e-mail notification I get.

      > If I get it right, the solution should look like this
      Basically yes. Five things to mention:
      - You don't need a default implementation (can be an abstract method)
      - You need an additional C function which registers "Ogre::Real getHeight(Ogre::Real x, Ogre::Real z)"
        at the native library (I would call that function in the Java constructor)
      - "Java_org_xbig_TreeLoader2D_getHeight" is a normal C function (-> it does not need all that JNI stuff)
      - The additional registering function needs that JNI stuff
      - The jni class and method id could be stored during registering (-> performance gain)

      > But do you think this could be a solution?
      Yes, this should work

      > Would it be hard to implement this into XBiG?
      I think so :(
      - First you have to recognize the callback methods you need (could be a list in a config file).
      - You have to generate the abstract java class and three C functions.
      - If parameters are no built in types you have to deal with memory/addresses
        (I would not support this -> only primitive types as parameters of callback functions)

      (- It should be generalized to support listeners
        (-> callbacks with C++ polymorphism instead of C function pointers, used often in OGRE))

      Okay, when putting many stuff in a config file it could be much easier to implement (it would be still a lot to do). But that means you have to maintain that config file for each project and it is not really "auto generated".

      I do not have time to implement this in the next few month :(
      -> you have to do it on you own
      -> you have to learn XSLT

       
      • scepticx

        scepticx - 2007-12-11

        Hi,

        I think I made it so far, but I don't think that I would be very in implementing it to xbig, but I will try if I find some time.
        What is the main difference I have to change for C++ polymorphism?

        greets

        ole

         
        • Christoph Nenning

          > I think I made it so far, but I don't think that I would be very in implementing it to xbig, but
          > I will try if I find some time.
          would be great

          > What is the main difference I have to change for C++ polymorphism?
          As example, let us look at Ogre::LogListener.
          When you want such a LogListener in Java, you have to write a native LogListener class which uses JNI to call methods of a Java LogListener. It would be convenient if construction of a Java LogListener would construct a C++ LogListener and if the Java LogListener could be passed to a register method ( Ogre::Log::addListener(LogListener *listener) ).

          The main difference is the native class.

           

Log in to post a comment.