Network I/O via Pack/UnPack (PUP)

CS 641 Lecture, Dr. Lawlor

/* 
CS 641 example of my strange "pup" design pattern.

Dr. Orion Sky Lawlor, lawlor@alaska.edu, 2012-04-18 (Public Domain)
*/
#include <stdio.h>
#include <iostream>

#include <vector>
#include <map>
#include <string>

#include <stdlib.h>
#include <unistd.h>

/********** A "pupper" reads or writes an object's fields. *******/
class send_pupper {
int fd;
public:
enum {unpacking=0};
send_pupper(int fd_) { fd=fd_; }
friend void pup(send_pupper &p,const char *name,int &v) {
write(p.fd,&v,sizeof(v));
}
friend void pup(send_pupper &p,const char *name,char *data,int length) {
write(p.fd,data,length);
}
};
class recv_pupper {
int fd;
public:
enum {unpacking=1};
recv_pupper(int fd_) { fd=fd_; }
friend void pup(recv_pupper &p,const char *name,int &v) {
read(p.fd,&v,sizeof(v));
}
friend void pup(recv_pupper &p,const char *name,char *data,int length) {
read(p.fd,data,length);
}
};
class print_pupper {
public:
enum {unpacking=0};
friend void pup(print_pupper &p,const char *name,int &v) {
std::cout<<"int "<<name<<"="<<v<<"\n";
}
friend void pup(print_pupper &p,const char *name,char *data,int length) {
std::cout<<"data: "<<name<<" = "<<data<<"\n";
}
};

/************* Templated "pup" functions pass an object's fields to the pupper. ********/
template <class pupper>
void pup(pupper &p,const char *name,std::string &s) {
int length=s.size();
pup(p,"string length",length);
if (length<0 || length>10000000) {
printf("HELP! POLICE! length=%d\n",length);
return;
}
s.resize(length);
pup(p,"string data",&s[0],length);
}

template <class pupper,typename T>
void pup(pupper &p,const char *name,std::vector<T> &v) {
int length=v.size();
pup(p,"vector length",length);
if (length<0 || length>1000000000) {
printf("HELP! POLICE! length=%d\n",length);
return;
}
v.resize(length);
for (int i=0;i<length;i++) {
pup(p,"vector element",v[i]);
}
}

template <class pupper,typename KEY,typename VAL>
void pup(pupper &p,const char *name,std::map<KEY,VAL> &v) {
if (p.unpacking) {
while (1) {
int keep_going=1;
pup(p,"more",keep_going);
if (!keep_going) break;

KEY key;
VAL val;
pup(p,"map key",key);
pup(p,"map val",val);
v[key]=val;
}
}
else { /* v is valid, not unpacking */
int keep_going=1;
for (typename std::map<KEY,VAL>::iterator it=v.begin();
it!=v.end();
++it)
{
pup(p,"more",keep_going);
KEY key=(*it).first;
VAL val=(*it).second;
pup(p,"map key",key);
pup(p,"map val",val);
}
keep_going=0;
pup(p,"more",keep_going); // that's the last key/value pair
}

}



class student {
public:
std::string name;
int ID;
std::map<int,std::vector<std::string> > courses;

template <class pupper>
friend void pup(pupper &p,const char *name,student &s) {
pup(p,"name",s.name);
pup(p,"ID",s.ID);
pup(p,"courses",s.courses);
}

};


int main()
{
int p[2]; /* ends of the pipe: 0 for reading, 1 for writing */
pipe(p); /* make a pipe */
if (0==fork()) { /* I am the child--send data to parent */
student s;
s.name="Sven"; s.ID=30000666;
for (int i=0;i<20;i++) {
char c[2];
c[0]='A'+(rand()%26);
c[1]=0;
s.courses[1990+(rand()%20)].push_back(c);
}
send_pupper P(p[1]);
pup(P,"student",s);
printf("Child sent pipe data.\n");
} else { /* I am the parent--receive child data */
student s;
printf("Parent: reading from pipe\n");
recv_pupper P(p[0]);
pup(P,"student",s);
printf("Parent: back from pipe\n");
std::cout<<"student: "<<s.name<<" ("<<s.ID<<")\n";
std::cout<<"Size of course list: "<<s.courses.size();

print_pupper pp;
pup(pp,"student",s);
}
return 0;
}

(Try this in NetRun now!)