2

I am building a small app on a raspberry pi. I have a JVM which tries to access a C++ Library called "RCSwitch" I created a JavaClass

public class NativeRCSwitchAdapter {

    private static final NativeRCSwitchAdapter instance = new NativeRCSwitchAdapter();

    public static NativeRCSwitchAdapter getInstance(){
        return instance;
    }
    private NativeRCSwitchAdapter(){};

    static{
        String path = NativeRCSwitchAdapter.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        System.load(path + "NativeRCSwitchAdapter.so");
}

    // methods to redirect to native layer (C++)
    public native void switchOn(String group, String channel);
    public native void switchOff(String group, String channel);
}

I then ran javac & javah to have java generate my header file for me.

I created a c++ file:

#include "NativeRCSwitchAdapter.h"
#include "RCSwitch.h"
#include <stdio.h>
#include <iostream>

using namespace std;

JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
cout<<"teststring output"<<endl;
const char *csGroup = env->GetStringUTFChars(jsGroup, 0);
const char *csChannel = env->GetStringUTFChars(jsChannel, 0);

char sGroup[6];
char sChannel[6];

for (int i = 0; i<5; i++) {
    sGroup[i] = csGroup[i];
    sChannel[i] = csChannel[i];
}
sGroup[5] = '\0';
sChannel[5] = '\0';

cout<<"ONON"<<endl;
cout<<sGroup<<endl;
cout<<sChannel<<endl;


RCSwitch mySwitch = RCSwitch();

//for testing purposes set to the ELRO Power Plugs
mySwitch.setPulseLength(300);
mySwitch.enableTransmit(0);
mySwitch.setRepeatTransmit(3);

mySwitch.switchOn(sGroup, sChannel);
}

Now this file uses the RCSwitch library which in turn uses the wiringPi library.

Now if i compile i run this:

g++ -shared -I/usr/jdk1.8.0/include -I/usr/jdk1.8.0/include/linux NativeRCSwitchAdapter.cpp -o NativeRCSwitchAdapter.so

Yet I get this error if start everything from java: (simple main, create an instance of my object and run the switchOn()

java: symbol lookup error: /home/pi/applications/Pi-jAutomation433/RCSwitchJNIWrapper/src/NativeRCSwitchAdapter.so: undefined symbol: _ZN8RCSwitchC1Ev

It has been time, since i last coded in C, so please forgive me but I believe it has something to do with the the linking phase of the compiler? Or does the compiler automatically check all dependencies and then their deps until no further dependencies are found and it then links it all nicely together and wraps it in an app?

Oh here is the repo to have an in depth look if anybody cares: Github repo

Thanks for any help coming my way!

UPDATE

Okay so I managed to get this error away. Turns out (well I kinda knew that already but yeah) I am quiet a duphus when it comes to C++ compiler knowledge. Anyways I managed to get the error changed. I didn't know I had to explicitly tell g++ to include RCSwitch.cpp as well. Okay so now I did. Next error ;-) I guess this time it should be fairly easy to tackle. I get an undefined symbol "pinMode". This symbol is part of the wiringPi library. Do I have to include ALL c librarys that are executed in my java file? Or only the one I access and anything after that doesnt matter to java?

pascalwhoop
  • 2,984
  • 3
  • 26
  • 40
  • Where is `RCSwitch()` defined? If it's in some library then you will need to either figure out how to link your custom library such that the other library is included, is referenced in such a way that the dynamic loader automatically brings it in as well, or else simply manually load the other library from Java before you load your custom library which needs it. If that library is not a C++ one, you will also need to be sure it's headers are `extern "C" {}`. – Chris Stratton Sep 30 '13 at 17:30

1 Answers1

-1

Your native function declaration is getting mangled by the c++ compiler. Add extern "C" around your declarations to clear up the issue.

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
#ifdef __cplusplus
}
#endif  /* __cplusplus */

Edit: You need to include all other objects/libraries into your creation of the shared library.

See this Dynamic Link Library Q/A.

Community
  • 1
  • 1
Samhain
  • 1,767
  • 1
  • 12
  • 20
  • This can indeed be an issue, but it does not seem to match the runtime error message. It's not the JNI function with it is failing to find. – Chris Stratton Sep 30 '13 at 17:28
  • 1
    Good point. My guess is with the syntax `RCSwitch mySwitch = RCSwitch();` he's trying to call the no-arg constructor. That should just be `RCSwitch mySwitch;` – Samhain Sep 30 '13 at 17:37
  • This is not necessary. It is done in the generated header file, which is included in his .cpp file, which is sufficient to tell the compiler that this method is `extern "C"`. Furthermore this is not the source of his error. And `RCSwitch mySwitch;` is identical to `RCSwitch mySwitch = RCSwitch();` -1 – user207421 Oct 01 '13 at 02:08
  • `g++ -shared -I/usr/jdk1.8.0/include -I/usr/jdk1.8.0/include/linux NativeRCSwitchAdapter.cpp -o NativeRCSwitchAdapter.so` Is the implementation of the RCSwitch class within your NativeRCSwitchAdapter.cpp file? If not, then you need to link that compiled object into your .so, or load that library as well. – Samhain Oct 01 '13 at 13:21
  • Okay so the compiler doesn't automatically look for the dependencies (i am from spring, there dependency injection happens automatically). How do I put "all" libraries I need into the .so as well or otherwise link them to my NativeRCSwitchAdapter.so? – pascalwhoop Oct 01 '13 at 13:24
  • In response to your question edit, yes, you need to add all of the objects and static libraries into your .so shared library. See -> [this question](http://stackoverflow.com/questions/2649735/how-to-link-static-library-into-dynamic-library-in-gcc) – Samhain Oct 01 '13 at 14:22