-2

We are trying to create a java application that uses a C++ executable that performes an algorimth seperately. To have java and C++ communication we use cout and getline in C++ and while true loop with reader.readline in java. The problem arise when using cygwin as compiler for C++. As soon as java starts it receives null from c++ continuously. We are at a loss of what it problem is as the while loop had worked previously.

Java part

import java.io.*;


public class Main {
    private static ProcessBuilder pb;
    private static Process process;
    private static BufferedReader reader;
    private static BufferedWriter writer;

    public static void main(String[] args) throws IOException{//TransformException
        pb = new ProcessBuilder();
        pb.command("C:/Users/*USERNAME*/CLionProjects/untitled/cmake-build-debug/untitled.exe");  // C++ executable
        process = pb.start();
        reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));

        ReaderThread rd = new ReaderThread(reader);
        rd.start();

        while(true){
            String input = inputReader.readLine();
            String lineToSend;
            switch(input.toLowerCase()){
                case "run":
                    String arg1 = "first";
                    String arg2 = "second";
                    lineToSend = "runExample"+" " + arg1 + " "+  arg2 + "\n";
                    writer.write(lineToSend);
                    writer.flush();
                    break;
            }
        }
    }
}


import java.io.IOException;

class ReaderThread extends Thread {
    BufferedReader reader;

    public ReaderThread(BufferedReader reader) {
        this.reader = reader;
    }

    public void run() {
        try {
            String reply;
            System.out.println("Reader thread ready");
            while (true) {
                reply = reader.readLine();
                String[] replyAsArr = {"No reply"};
                if (reply != null) {
                    replyAsArr = reply.split(" ");
                } else System.out.println("Got reply: " + reply);
                switch (replyAsArr[0]) {
                    case "result":
                        System.out.println("Got result");
                        break;
                    case "info":
                        System.out.println(reply);
                        break;
                    default:
                        System.out.println("Malformed input from cpp: " + reply);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

C++ part (CppPart.exe)

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <map>

using namespace std;

void runAlgorithm() {
    cout << "result" << endl;
    cout << flush;
}

void communicateWithJava() {
    enum commands{
        ERROR,
        runExample,
    };
    map<string, commands> mapStringToEnum =
            {
                    {"error", ERROR},
                    {"runExample", runExample},
            };
    string line;
    while(getline(cin, line)) {
        istringstream buf(line);
        istream_iterator<string> beg(buf), end;
        vector<string> lineAsTokens(beg, end);
        commands switchType = mapStringToEnum[lineAsTokens[0]];
        switch (switchType) {
            case ERROR: {
                cout << "Could not understand input: "<< line << endl;
                cout << flush;
                break;
            }
            case runExample: {
                runAlgorithm();
                cout << "info " << lineAsTokens[1] << lineAsTokens[2] << endl;
                cout << flush;
                break;
            }
        }
    }
}

int main() {
    communicateWithJava();
    return 0;
}

  • 1
    There is no Java machine for Cygwin, so the two programs use different paradigma. Try compiling the C++ with Mingw http://mingw-w64.org/ – matzeri May 19 '21 at 12:42
  • I have just tried it with mingw but still continuously receive nulls from our thread. – Svend Christensen May 19 '21 at 12:49
  • 1
    What is `runAlgorithm`? Please post a [mcve]. – PaulMcKenzie May 19 '21 at 12:53
  • 1
    How is this both a Java and a C++ question? Either the Java code is correct and you have a faulty C++ program, or vice-versa. You can just run the C++ program on its own to find out which is the case. – Useless May 19 '21 at 13:11
  • I have updated the code with something that should reproduce the problem. Just starting the java code should show the continuous nulls being read. As for the languages running the programs seperately does not seem to reproduce the problem on either side. – Svend Christensen May 19 '21 at 13:23
  • @PaulMcKenzie The code above should be a reproducible example with cygwin as compiler for C++. – Svend Christensen May 19 '21 at 13:35
  • All the C++ program does is write to the console. I don't see how Java is supposed to communicate with C++'s writing to the console. Have you personally seen any Java program that communicates with C++ (or really any external executable) in this fashion? It's usually done by using pipes, files, etc., not console output. – PaulMcKenzie May 19 '21 at 16:45
  • @PaulMcKenzie I have updated the code above so that you can type run in java which will pass arg1 and arg2 to c++ as it also calls runExample and pass back the arguments to the reader thread. This works if the compiler for C++ is visual studio but not if the compiler is cygwin – Svend Christensen May 20 '21 at 12:14
  • If you're passing arguments to C++, your `int main()` should be `int main(int argc, char *argv[])`, and then process `argc` and `argv`. That's why it seems strange why you thought this code could ever work, regardless of the compiler. – PaulMcKenzie May 20 '21 at 12:26
  • @PaulMcKenzie as stated in my previous comment when compiler with visual studio as compiler the code does indeed work and will pass arg- 1 and 2 to C++ which runs in a thread along side java. Our problem is that the code does not work when compiled with cygwin or mingw. – Svend Christensen May 20 '21 at 12:43
  • So you were *lucky* that it worked with Visual Studio. That's the point I'm trying to make. Why not, just for laughs, try to actually inspect `argc` and `argv`? If you see your argument count and arguments in argc and argv, then what you were supposed to have done originally was to use what C++ always uses to process command-line arguments, and not something that, to me, looks like a hack. – PaulMcKenzie May 20 '21 at 13:26
  • @PaulMcKenzie Yes I do believe what you are explaining would be a better choice and it might not be possible to do what this code attempts. However, in our program we need to initialize a lot of stuff in C++ so starting the program every time we need to run the algorithm would take a long time. What we would like to be able to do it have the C++ program waiting for a request and for it to reply to that request with an output. Our problem is that is needs to be somewhat fast and the output is rather large. We are very new to C++ so if you have any recommendations we would really appriciate it. – Svend Christensen May 20 '21 at 14:06
  • @SvendChristensen [IPC between Java and C++](https://stackoverflow.com/questions/14486754/communication-between-java-and-c-process). Note that MingW is basically g++. The g++ compiler is one of the most used in the industry, next to Visual Studio. Thus it makes more sense to do something that works for g++, and as a byproduct, will work for Visual Studio (and probably also any other compiler, such as clang). – PaulMcKenzie May 20 '21 at 14:44
  • @PaulMcKenzie From the link you sent one of the possible methods for communication is Standard input output channels. From our understanding this is what we are trying to do. When we in java call `process.getInputStream()` we obtain the output stream buffer from C++, when we try to access this stream(compiled with g++) we can endlessly read null and nothing else, however when we have compiled our C++ program with MSVC we can read from the terminal output as we expect. – Svend Christensen May 20 '21 at 15:15
  • For the C++, are you sending `std::string`? If you are, you should be aware that `std::string` is not a type that you can send. It is a C++ object whose internals can differ between compilers. The `c_str()` member function (which I don't see at all in your code) is the underlying character buffer of the `std::string` that you would utilize. – PaulMcKenzie May 20 '21 at 15:23
  • We are sending `std::string` however if all couts in C++ are commented out we still receive nulls on `reader.readline()`. – Svend Christensen May 20 '21 at 17:26
  • @SvendChristensen Then maybe sending `std::string` is the reason for the problem. The internals of a `std::string` may use [short-string optimizations](https://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring). That means that what you are receiving in Visual Studio just, by chance, is a static buffer that holds around 20 characters. If that parameter were 50 or more characters, maybe you will get the same issue with Visual C++ as with the other compiler. – PaulMcKenzie May 21 '21 at 12:40
  • It doesn't 'output null continuously'. `null` is a sentinel value returned by `readLine()` when the peer has closed the connection. It indicates end of stream, on which you shoudl cease reading and close the input stream. See the Javadoc. Instead you are just continuing the read loop. Basic programming error in the Java code. – user207421 May 27 '21 at 04:24

0 Answers0