There is a program written in C ++. There is a script written in the Python language (in fact, it is not so important in what language). It is necessary to arrange the transfer of data between the processes of these programs. OS with GNU / Linux kernel.

I saw different options, but basically always Python acted as a startup application that started the program process in C ++. On the contrary, it is necessary for the C ++ process to run Python scripts.

It is desirable that the method be universal, that is, so that you can write a script in another language and the same method to exchange data with it.

  • 3
    named pipe try it ... - Vladimir Martyanov
  • one
    Do you have a question "what IPC methods exist"? Or "how to run the program in c ++ compatible with the selected IPC method"? Which method to choose depends on your program, what data and how specifically you want to exchange information (where the simple pipe () works, where the socket is, where the D-Bus is used, etc.). - jfs
  • Related question: Get the I / O process - jfs
  • @jfs I had a problem with the sequence of running processes. I could not find the info where the Python script would run after C ++. I think in my case the named pipe is quite suitable. Thank! - AccumPlus
  • @ Vladimir Martianov Thank you! I will try. - AccumPlus

3 answers 3

If you want to exchange messages with a script from a C / C ++ program in Linux, just like you can do it sitting at a terminal (i.e., if the script reads stdin and writes to stdout), you can use this function

#include <stdio.h> #include <stdlib.h> #include <termios.h> #include <unistd.h> #include <pty.h> FILE * pty_execvp (char *argv[], pid_t *child) { int pty; struct termios t; cfmakeraw(&t); if (!(*child = forkpty(&pty, 0, &t, 0))) { execvp(argv[0], argv); fprintf(stderr, "exec %s: %m\n", argv[0]); exit(127); } else if (*child == -1) return 0; return fdopen(pty, "r+"); } 

Similar to popen, it returns the FILE * associated with a pseudo-terminal, on which a script (program) with specified arguments is run in a new process.

The name of the file with the script and its arguments are passed as if to the execvp function (only the first argument of the script is taken for the name of the file being run).

The address in the child argument is the pid of this process.

You need to compile with the -lutil key (see man forkpty ).

Here is a (somewhat cumbersome and artificial) example of its use.

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <poll.h> FILE *pty_execvp (char *argv[], pid_t *child); int fd_out (int fd, FILE *out, int tmout) { struct pollfd fds = {fd, POLLIN}; char buf[1024]; int l, s = 0; while (poll(&fds, 1, tmout) > 0) { if ((l = read(fd, buf, 1024)) > 0) { s += l; fwrite(buf, 1, l, out); } else return -1; } return s; } int main (int ac, char *av[]) { char *line = 0; size_t lsz; const char *cmd = "ls -l *.txt"; FILE *in = popen(cmd, "r"); if (!in) perror("popen"), exit(2); const char *pcmd[] = { "grep", "20", 0 }; pid_t cpid; FILE *pty = pty_execvp((char **)pcmd, &cpid); if (pty) printf("run %s pid %ld\n", pcmd[0], (long)cpid); else exit(fputs("pty-exec error", stderr)); while (getline(&line, &lsz, in) > 0) { fputs(line, pty); // printf("> %s", line); if (fd_out(fileno(pty), stdout, 0) < 0) { perror("fd_out"); break; } } while (fd_out(fileno(pty), stdout, 1) > 0); int rc; rc = pclose(in); printf("%s rc = %d\n", WIFEXITED(rc) ? "exit" : "terminated", WEXITSTATUS(rc)); fclose(pty); pid_t fin = wait(&rc); if (WIFEXITED(rc)) printf("Exit pid:%ld %d\n", (long)fin, WEXITSTATUS(rc)); else if (WIFSIGNALED(rc)) printf("Signal pid:%ld %d\n", (long)fin, WTERMSIG(rc)); else printf("??? pid:%ld %d\n", (long)fin, rc); return puts("End") == EOF; } 

This example gives the same result as ls -l *.txt | grep 20 ls -l *.txt | grep 20 and demonstrates how to use popen with pty_exec for the case when we do not know in advance what the response from grep to the transmitted string will be.

Note the timeout in the fd_out() call at the end of the program.

    Implementation options, indeed, several. Named pipes (named pipes) were suitable for my task.

    An example implementation is presented below. Some points of error handling are omitted, and the code itself is simplified.

    C ++ snippet

     int pipeDescr, bytesNumber; std::string outputPipeName{"inputPipe"}, inputPipeName{"outputPipe"}; char message[BUFSIZ]; if (mkfifo(inputPipeName.c_str(), 0777) || mkfifo(outputPipeName.c_str(), 0777)) { perror("mkfifo"); return 1; } memset(message, '\0', BUFSIZ); strcpy(message, "Sample message"); if ((pipeDescr = open(outputPipeName.c_str(), O_WRONLY)) <= 0) { perror("open"); return 2; } bytesNumber = write(pipeDescr, message, strlen(message) + 1); if (bytesNumber <= 0) { perror("write"); return 3; } close(pipeDescr); memset(message, '\0', BUFSIZ); if ((pipeDescr = open(inputPipeName.c_str(), O_RDONLY)) <= 0) { perror("open"); return 2; } bytesNumber = read(pipeDescr, message, BUFSIZ); if (bytesNumber <= 0) { perror("read"); return 4; } close(pipeDescr); std::cout << "Got message: " << message << std::endl; remove(inputPipeName.c_str()); remove(outputPipeName.c_str()); 

    Python code snippet

     input_pipe_name = "inputPipe" output_pipe_name = "outputPipe" while not os.path.exists(input_pipe_name) and not os.path.exists(output_pipe_name): pass try: pipe_descr = os.open(input_pipe_name, os.O_RDONLY) mes = os.read(pipe_descr, 1024) os.close(pipe_descr) print('Got: ' + str(mes)) mes = 'Some answer' pipe_descr = os.open(output_pipe_name, os.O_WRONLY) os.write(pipe_descr, bytes(mes, 'UTF-8')) os.close(pipe_descr) except OSEerror as exc: print('Exception: ' + str(exc)) 

    The first is the process of a program written in C ++, since it creates channel files.

      Judging by your question about signals , I would simplify the interaction of processes, for example, you can write and read using standard input / output in Python:

       #!/usr/bin/env python3 import sys for line in sys.stdin: print(work_func(line), flush=True) 

      To redirect the I / O of the child process in C after fork() you can dup2() be used . Or even easier, using socketpair . If required, you can use shared memory ( mmap , posix_ipc ).