Q:

How to correctly receive data from pipes?

So I wrote a program using pipes and execl to do some arithmetic. I'm not sure how to debug pipes and different processes, but from what I can see it has to have something to do with my pipes reading the numbers. So my setup so far is the children are created, and based on thier given operation, they execl to different programs that will calculate the written values in the pipe. I don't know if I should implement a wait or something in the parent after it has written its values to the children before it reads the result. I keep getting Z as my output.
So this is one of the files I'm execl to and the other files look exactly the same it just shows its corresponding operation:
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>

using namespace std;

main(int argc, char *argv[])
{
    int x, y, z;
    while (read(0, (char *)&x, sizeof(int)) &&
           read(3, (char *)&y, sizeof(int))) {
        z = x * y;
        if (argc > 1)
            cerr << "multiply: " << x << " * " << y << " = " << z << endl;
        write(1, (char *)&z, sizeof(int));
    }
}

This is my code, which would actually be doing the calculating, which I think the pipes are set up correctly. Just a little explanation on the pipe setup, to save time. Child 0 -- reads from pipe 0 1 and writes to 2.  Child 1 reads from pipe 2 and 3 and writes to 4. The parent writes to 0 1 3  and reads from 4. My code:
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <sstream>

using namespace std;

const int MAX =21;
int pipes[MAX][2];
char operations[MAX];

int main()
{
    int a=0;
    int n;
    fstream datafile;
    pid_t pid;
    string line;
    datafile.open("data1.txt");
   

   if(datafile)
    {

        string input;
        getline(datafile, input);
        
        for(int i=0; i< input.size(); i++)
        {

    
            if (input[i] == '*'|| input[i] == '/'||input[i] == '+'||input[i] == '-')
            {
      
                operations[a] = input[i];
                a++;

            }

        }
      n = (a);

        for(int i =0; i<(2*n+1); i++)
           pipe(pipes[i]);

        for (int i=0; i <n; i++)
        {    
           pid = fork();
            if(pid == 0)
            {
             
                close(0);
                dup(pipes[2*i][0]);
                close(3);
                dup(pipes[2*i+1][0]);
                close(1);
                dup(pipes[2*i+2][1]);
              
            
                    switch(operations[i])
                    {
                    case '+':
                        execl("add","add", NULL);
                    case '-': 
                        execl("subtract","multiply", NULL);
                    case '*':
                        execl("multiply","multiply", NULL);
                    case '/':
                        execl("divide","divide", NULL);
                    
                    }

                 cout<< "No match for operation!";
            }
            else if(pid <0)
            cerr<<"Fork has failed";
          }
    
        int x, y, z;

        for(int i=0; i<3; i++)
          {
            getline(datafile,line);
            istringstream ins(line);
            ins >> x >> y >> z;

            write(0,(char *)&x, sizeof(x));
            write(3,(char *)&z, sizeof(z));
            write(1,(char *)&y, sizeof(y));
          }
          
          close(0);
          close(3);
          close(1);
        }  
        
            else
                cerr<<"Error reading file";
          

          datafile.close();
          
            int result;
            while(read(pipes[2*n][1],(char *)&result,sizeof(result)))
                cout<<result<<endl;
    
        
        
 
        

}

datafile can be this:

a * b *c
30 40 50
100 3 6

A:

Let's simplify this code. No operations -- still doesn't work. Only one child -- still doesn't work. Only one pipe -- still doesn't work. No file I/O -- still doesn't work. We are now trying to pass the number 30 from the parent to the child:
  int pipes[1][2];

  pid_t pid;

  pipe(pipes[0]);

  pid = fork();
  if(pid == 0)
    {
      cout << "child here" << endl;
      close(0);
    }
  else if(pid <0)
    cerr<<"Fork has failed";

  cout << "parent here " << endl;

  int x = 30;
  write(0,(char *)&x, sizeof(x));
  close(0);

  int result;
  read(pipes[0][1],(char *)&result,sizeof(result));
  cout << "result is " << result << endl;

See the bugs? Let's go one step further, and remove the pipe communication entirely:
pid_t pid;
pid = fork();
if(pid == 0)
  {
    cout << "child here" << endl;
  }
else if(pid <0)
  cerr<<"Fork has failed";

cout << "parent here " << endl;

See the bug? We fix it:
pid_t pid;
pid = fork();
if(pid == 0)
  {
    cout << "child here" << endl;
    return(0);
  }
else if(pid <0)
  cerr<<"Fork has failed";

cout << "parent here " << endl;

And put the pipe back in:
  int pipes[1][2];

  pid_t pid;

  pipe(pipes[0]);

  pid = fork();
  if(pid == 0)
    {
      cout << "child here" << endl;

      close(0);

      int result;
      read(pipes[0][1],(char *)&result,sizeof(result));
      cout << "result is " << result << endl;
      return(0);
    }
  else if(pid <0)
    cerr<<"Fork has failed";

  cout << "parent here " << endl;

  int x = 30;
  write(0,(char *)&x, sizeof(x));
  close(0);

See the bug? Fix the pipe bug, then work back up to 2 children and 5 pipes. Test the operation stuff separately, then splice it in.
I trust this demonstrates the power of simplifying code.

