HOWTO: Converting Simple OpenGL Code from C++ to Perl

Over the last few weeks I’ve been trying to learn OpenGL. However, I’ve had a miserable time trying to get various implementations of it to work on my Mac, notably with C++ and Java. I was surprised to learn that installing the Perl OpenGL implementation (or POGL) is pretty simple, and that converting OpenGL code written in C++ to Perl is also reasonably easy. The following is a heuristic method you can use to do the same, using an example from the OpenGL SuperBible by Richard S. Wright, Jr. The code is freely available from his site at http://www.starstonesoftware.com/OpenGL/, and feel free to use my Perl conversion of his code.

This ended up being longer than I thought, so I’ll put a general summary here and then dive into it:
1. Install OpenGL support for Perl (POGL)
2. Change the comments from // to #
3. Import the POGL library
4. Change declarations of GL data types to just scalars.
5. Convert subroutine styles from C++ to Perl
6. Change all uses of a variable with $variable.
7. Add curly braces in one-line “if” blocks and convert “else if” to “elsif”
8. Remove the opening declaration of the main function and it’s return value
9. Change the GLUT instantiation line (the first line of the main method)
10. If functions are passed around, add \& to pass their references.

If you have Mac OS X, Perl is already installed and good to go. To install POGL, open a Terminal and type the following:
sudo cpan
install OpenGL
quit

Pretty simple! Running this as root is recommended, otherwise some of the files needed to update your basic Perl install may not go well. I said “yes” to all the optional installs and it was smooth sailing!

With that out of the way, let’s begin by analyzing Wright’s C++ OpenGL code:

// Stencil.cpp
// OpenGL SuperBible
// Richard S. Wright Jr.
// rwright@starstonesoftware.com

#include “../../shared/gltools.h” // OpenGL toolkit

// Initial square position and size
GLfloat x = 0.0f;
GLfloat y = 0.0f;
GLfloat rsize = 25;

// Step size in x and y directions
// (number of pixels to move each time)
GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;

// Keep track of windows changing width and height
GLfloat windowWidth;
GLfloat windowHeight;

///////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
GLdouble dRadius = 0.1; // Initial radius of spiral
GLdouble dAngle; // Looping variable

// Clear blue window
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);

// Use 0 for clear stencil, enable stencil test
glClearStencil(0.0f);
glEnable(GL_STENCIL_TEST);

// Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// All drawing commands fail the stencil test, and are not
// drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);

// Spiral pattern will create stencil pattern
// Draw the spiral pattern with white lines. We
// make the lines white to demonstrate that the
// stencil function prevents them from being drawn
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
for(dAngle = 0; dAngle < 400.0; dAngle += 0.1)
{
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();

// Now, allow drawing, except where the stencil pattern is 0x1
// and do not make any further changes to the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

// Now draw red bouncing square
// (x and y) are modified by a timer function
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y – rsize);

// All done, do the buffer swap
glutSwapBuffers();
}

///////////////////////////////////////////////////////////
// Called by GLUT library when idle (window not being
// resized or moved)
void TimerFunction(int value)
{
// Reverse direction when you reach left or right edge
if(x > windowWidth-rsize || x < -windowWidth)
xstep = -xstep;

// Reverse direction when you reach top or bottom edge
if(y > windowHeight || y < -windowHeight + rsize)
ystep = -ystep;

// Check bounds. This is in case the window is made
// smaller while the rectangle is bouncing and the
// rectangle suddenly finds itself outside the new
// clipping volume
if(x > windowWidth-rsize)
x = windowWidth-rsize-1;

if(y > windowHeight)
y = windowHeight-1;

// Actually move the square
x += xstep;
y += ystep;

// Redraw the scene with new coordinates
glutPostRedisplay();
glutTimerFunc(33,TimerFunction, 1);
}

///////////////////////////////////////////////////////////
// Called by GLUT library when the window has chanaged size
void ChangeSize(int w, int h)
{
GLfloat aspectRatio;

// Prevent a divide by zero
if(h == 0)
h = 1;

// Set Viewport to window dimensions
glViewport(0, 0, w, h);

// Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// Establish clipping volume (left, right, bottom, top, near, far)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
{
windowWidth = 100;
windowHeight = 100 / aspectRatio;
glOrtho (-100.0, 100.0, -windowHeight, windowHeight, 1.0, -1.0);
}
else
{
windowWidth = 100 * aspectRatio;
windowHeight = 100;
glOrtho (-windowWidth, windowWidth, -100.0, 100.0, 1.0, -1.0);
}

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

///////////////////////////////////////////////////////////
// Program entry point
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow(“OpenGL Stencil Test”);
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutTimerFunc(33, TimerFunction, 1);
glutMainLoop();

return 0;
}

Note: There are no notes in the downloadable files on Wright’s site as to the license that this code is released under, nor have I asked him for his permission. However, as it is free (as in beer) to download it, I do not see a problem showing it to you here.

Begin by replacing C style comments with Perl style comments: Replace all instances of // with #. There may be a leftover /# that is not replaced correctly, look for those and replace those with ##.

Next, import the POGL libraries / packages by taking out this C++ line:
#include "../../shared/gltools.h" # OpenGL toolkit

And by putting these lines at the very top of your program:
#!/usr/bin/perl -w
use strict;
use OpenGL qw/ :all /;
use Math::Trig;

You don’t always need the Trigonometry libraries, but with graphic manipulation (especially spheres!) it couldn’t hurt. Next, replace all declarations of the GL data types with “my $”, and for floating point numbers, remove the “f” off the end if it is there. For example,
GLfloat x = 0.0f; becomes my $x = 0.0;
Don’t forget to remove any casts to GL data types. Also, find all instances where a float is used C++ style (0.0f) and change those to Perl style (0.0).

Next, let’s straighten up the subroutines. Over the last few weeks all the C++ OpenGL code I’ve seen either takes in arguments or doesn’t, but have not returned any values (has a void return type). We can change those as follows:
Case 1: Takes no arguments:
void RenderScene(void) becomes sub RenderScene
Case 2: Takes in arguments:
void ChangeSize(int w, int h) { becomes
sub ChangeSize {
my ($w, $h) = @_;

Either way, it isn’t particularly difficult. Now comes the tedious part. Find any uses of a variable and add in a $ before it, so h = 0; becomes $h = 0;.

Next, find all your if statements and if they don’t have curly braces on them, add them (even if it’s just one line inside the if block). If the C++ code uses “else if” statements, change those to “elsif”.

Finally, remove the beginning of the main function and it’s closing curly brace:
int main(int argc, char* argv[])
{

is removed, and return 0; } is removed.

Next, we need to call the GLUT library “the Perl” way, so make this change:
glutInit(&argc, argv); becomes eval {glutInit(); 1} or die "This test requires GLUT:\n$@\n";

Let’s wrap it up by appending “” to function names when we give their names to other functions:
glutReshapeFunc(ChangeSize); becomes glutReshapeFunc(\&ChangeSize);

My final output looks like this:

#!/usr/bin/perl -w
use strict;
use OpenGL qw/ :all /;
use Math::Trig;

# Stencil.cpp
# OpenGL SuperBible
# Richard S. Wright Jr.
# rwright@starstonesoftware.com
# Converted to Perl by Chris Bunch

# Initial square position and size
my $x = 0.0;
my $y = 0.0;
my $rsize = 25;

# Step size in x and y directions
# (number of pixels to move each time)
my $xstep = 1.0;
my $ystep = 1.0;

# Keep track of windows changing width and height
my $windowWidth;
my $windowHeight;

##############################
# Called to draw scene
sub RenderScene
{
my $dRadius = 0.1; # Initial radius of spiral
my $dAngle; # Looping variable

# Clear blue window
glClearColor(0.0, 0.0, 1.0, 0.0);

# Use 0 for clear stencil, enable stencil test
glClearStencil(0.0);
glEnable(GL_STENCIL_TEST);

# Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

# All drawing commands fail the stencil test, and are not
# drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);

# Spiral pattern will create stencil pattern
# Draw the spiral pattern with white lines. We
# make the lines white to demonstrate that the
# stencil function prevents them from being drawn
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_STRIP);
for($dAngle = 0; $dAngle < 400.0; $dAngle += 0.1)
{
glVertex2d($dRadius * cos($dAngle), $dRadius * sin($dAngle));
$dRadius *= 1.002;
}
glEnd();

# Now, allow drawing, except where the stencil pattern is 0x1
# and do not make any further changes to the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

# Now draw red bouncing square
# (x and y) are modified by a timer function
glColor3f(1.0, 0.0, 0.0);
glRectf($x, $y, $x + $rsize, $y – $rsize);

# All done, do the buffer swap
glutSwapBuffers();
}

##############################
# Called by GLUT library when idle (window not being
# resized or moved)
sub TimerFunction {
my ($value) = @_;
# Reverse direction when you reach left or right edge
if($x > $windowWidth-$rsize || $x < -$windowWidth) {
$xstep = -$xstep; }

# Reverse direction when you reach top or bottom edge
if($y > $windowHeight || $y < -$windowHeight + $rsize) {
$ystep = -$ystep; }

# Check bounds. This is in case the window is made
# smaller while the rectangle is bouncing and the
# rectangle suddenly finds itself outside the new
# clipping volume
if($x > $windowWidth-$rsize) {
$x = $windowWidth-$rsize-1; }

if($y > $windowHeight) {
$y = $windowHeight-1; }

# Actually move the square
$x += $xstep;
$y += $ystep;

# Redraw the scene with new coordinates
glutPostRedisplay();
glutTimerFunc(33,\&TimerFunction, 1);
}

##############################
# Called by GLUT library when the window has chanaged size
sub ChangeSize {
my ($w, $h) = @_;
my $aspectRatio;

# Prevent a divide by zero
if($h == 0) {
$h = 1; }

# Set Viewport to window dimensions
glViewport(0, 0, $w, $h);

# Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

# Establish clipping volume (left, right, bottom, top, near, far)
$aspectRatio = $w / $h;
if ($w <= $h)
{
$windowWidth = 100;
$windowHeight = 100 / $aspectRatio;
glOrtho (-100.0, 100.0, -$windowHeight, $windowHeight, 1.0, -1.0);
}
else
{
$windowWidth = 100 * $aspectRatio;
$windowHeight = 100;
glOrtho (-$windowWidth, $windowWidth, -100.0, 100.0, 1.0, -1.0);
}

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

##############################
# Program entry point
eval {glutInit(); 1} or die “This test requires GLUT:\n$@\n”;
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow(“OpenGL Stencil Test”);
glutReshapeFunc(\&ChangeSize);
glutDisplayFunc(\&RenderScene);
glutTimerFunc(33, \&TimerFunction, 1);
glutMainLoop();

Whew! Try running it and see how it works! It takes a couple tries to get it right, but once you do, you’ve got a pretty good (although tedious) way to convert C++ OpenGL code to Perl! It takes longer than I’d like though, so I may have to write a script that does this for us…

One Response to HOWTO: Converting Simple OpenGL Code from C++ to Perl

  1. Omair says:

    Can you convert the code below i tried my best but nothing is happening

    #include // Windows TCP/IP socket support
    #include
    #include
    #include

    // NOTE: These are from the declarations in the SimPLEX software but with the
    // NO_SCENARIO state prepended to the start of the list, allowing
    // this state to be flagged. This may need modification in the future to match any
    // changes to the equivalent enumeration in the SimPLEX software – CHECK !!!
    enum State {NO_SCENARIO, INVALID_SCENARIO, INITIALISED, ARMING, RUNNING, PAUSED, ENDED, NUM_STATES };

    // Ideally the current state should be tested and commands only sent in valid states,
    // (e.g. The RU (run) command is only valid with the software in the INITIALISED state.)
    // However this is NOT done in this simple example program

    void SelectWinSock1_1();
    void CreateSocket();
    bool ConnectToServer();
    void SendData(char *message);
    void read_data ( char *data, int max_chars, int &num_chars_read);
    void tidyup();
    int get_status();

    SOCKET theSocket;
    SOCKADDR_IN theServerAddress;
    SOCKADDR_IN theClientAddress;
    int error;

    #define IN_MSG_LEN 300 // max size for status return

    int main(int argc, char* argv[])
    {
    bool quit = false;
    char status_c[IN_MSG_LEN+1];
    char mess[200];
    int num_chars_read;
    int trig_mode = 0; // Simulator hardware trigger modes
    int sim_status = -1; // Init to non-valid state
    bool relative_b = true; // Power adjust mode relative by default
    char scen_name[300];
    sprintf(scen_name, “my.scn”);

    printf(“SimPLEX remote socket interface test prog V1.01 31/1/2005\n\n”);

    // Set process priority to above normal
    if ( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
    {
    printf ( “Warning: Error setting process priority\n” );
    }

    printf (“Selecting winsock 1.1\n”);
    // Select Sockets Version 1.1 as it is more generic than Sockets 2.0
    SelectWinSock1_1();

    printf (“Creating socket\n”);
    // This call creates 12 separate sockets
    // (Sockets[NUM_CHANS]) one for each channel
    CreateSocket();

    printf (“Connecting to server… “);
    // This call connects them to the server
    if (ConnectToServer())
    {
    printf (” …connected!\n”);
    }
    else
    {
    quit = true;
    printf (“….exiting……\n”);
    Sleep(3000); // 3 secs wait so you can see exit message
    }
    while (!quit)
    {
    // Options menu:
    printf(“\n 1. Enter new scenario name\n”);
    printf(” 2. Enter new trig mode\n”);
    printf(” 3. Select scenario %s\n”, scen_name);
    printf(” 4. Set trig mode %d, then Run scenario \n”, trig_mode);
    printf(” 5. Stop simulation\n”);
    printf(” 6. Rewind simulation\n”);
    printf(” 7. Read simulation status\n”);
    printf(” 8. Power on/off\n”);
    printf(” 9. Power mode\n”);
    printf(” A. Power level\n”);
    printf(” B. PRN code on/off\n”);
    printf(” X. Quit program\n”);
    printf(“>”);

    char s[120];

    gets ( s );

    char item = s[0];

    if(item == ‘1’) // Enter new scenario name
    {
    printf(“\n Enter new scenario name (including path):\n”);
    // Note: if the user wants to specify a path
    // (e.g. c:\scenarios\my_scenario\my.sim) then the ‘\’ characters must be escaped
    // out i.e. user must actually enter c:\\scenarios\\my_scenario\\my.sim
    gets(scen_name); // get actual data

    }

    else if (item == ‘2’) // Select new trig mode
    {
    printf(“Enter trig mode (0=sw, 1=ext immediate, 2= ext delayed)>”);
    scanf(“%d”, &trig_mode);
    }

    else if (item == ‘3’) // Select named scenario
    {
    char command[210];
    sprintf (command, “SC,%s”, scen_name);
    SendData(command);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }

    else if(item == ‘4’) // Set trigger mode, then run program
    {
    sprintf(mess, “TR,%1d”, trig_mode);
    SendData(mess);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    printf(“Trig command sent\n”);

    /*
    send ‘RUN’ message,
    the run begins on the following 1 second tick
    */
    SendData(“RU”);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    printf(“Run command sent\n”);

    if (trig_mode != 0)
    {
    printf( “**Send external start pulse to start simulation**\n”);
    }
    }
    else if (item == ‘5’) // END message
    {
    SendData(“0 00:00:00,EN”);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’6′) // REWIND message
    {
    SendData(“RW”);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’7′) // read from SimPLEX
    {
    SendData(“NULL”);
    read_data ( status_c, IN_MSG_LEN, num_chars_read);
    if(num_chars_read > 0)
    {
    status_c[num_chars_read]=”; // null terminate return string
    printf (“\nStatus return:%s!\n”, status_c);
    }
    }
    else if (item==’8′) // POW_ON command
    {
    char s[120];

    bool all_chans_b = false;
    int tx_i = 0;
    bool by_chan_b = true;
    bool on_b = true;

    //

    printf ( “All channels [N] > ” );

    gets ( s );

    if ( s[0] == ‘y’ || s[0] == ‘Y’ ) all_chans_b = true;

    //

    if ( !all_chans_b )
    {
    printf ( “by channel, not TXID [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ ) by_chan_b = false;

    //

    if ( by_chan_b )
    printf ( “channel > ” );
    else
    printf ( “TXID > ” );

    gets ( s );

    sscanf ( s, “%d”, &tx_i );
    }

    //

    printf ( “on [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ ) on_b = false;

    //

    sprintf ( s, “-,POW_ON,v1_a1,%d,%d,%d,%d”, on_b, tx_i, by_chan_b, all_chans_b );

    SendData(s);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’9′) // Power Mode
    {
    char s[120];

    bool all_chans_b = false;
    int tx_i = 0;
    bool by_chan_b = true;

    //

    printf ( “All channels [N] > ” );

    gets ( s );

    if ( s[0] == ‘y’ || s[0] == ‘Y’ ) all_chans_b = true;

    //

    if ( !all_chans_b )
    {
    printf ( “by channel, not TXID [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ ) by_chan_b = false;

    //

    if ( by_chan_b )
    printf ( “channel > ” );
    else
    printf ( “TXID > ” );

    gets ( s );

    sscanf ( s, “%d”, &tx_i );
    }

    //

    printf ( “relative [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ )
    relative_b = false;
    else
    relative_b = true;
    //

    sprintf ( s, “-,POW_MODE,v1_a1,%d,%d,%d,%d”, relative_b, tx_i, by_chan_b, all_chans_b );

    SendData(s);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’A’ || item == ‘a’) // Power level
    {
    char s[120];

    bool all_chans_b = false;
    int tx_i = 0;
    bool by_chan_b = true;
    float level_f = 0.0;

    //

    printf ( “All channels [N] > ” );

    gets ( s );

    if ( s[0] == ‘y’ || s[0] == ‘Y’ ) all_chans_b = true;

    //

    if ( !all_chans_b )
    {
    printf ( “by channel, not TXID [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ ) by_chan_b = false;

    //

    if ( by_chan_b )
    printf ( “channel > ” );
    else
    printf ( “TXID > ” );

    gets ( s );

    sscanf ( s, “%d”, &tx_i );
    }

    //

    printf ( “new level > ” );

    gets ( s );

    sscanf ( s, “%f”, &level_f );

    //

    sprintf ( s, “-,POW_LEV,v1_a1,%g,%d,%d,%d,%d”, level_f, tx_i, by_chan_b, all_chans_b, !relative_b );

    SendData(s);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’B’ || item==’b’ ) // PRN codes ON/OFF command
    {
    char s[120];

    bool all_chans_b = false;
    int chan_i = 0;
    bool on_b = true;

    //

    printf ( “All channels [N] > ” );

    gets ( s );

    if ( s[0] == ‘y’ || s[0] == ‘Y’ ) all_chans_b = true;

    //

    if ( !all_chans_b )
    {
    printf ( “channel > ” );

    gets ( s );

    sscanf ( s, “%d”, &chan_i );
    }

    //

    printf ( “PRN code on [Y] > ” );

    gets ( s );

    if ( s[0] == ‘n’ || s[0] == ‘N’ ) on_b = false;

    //

    sprintf ( s, “-,PRN_CODE,%d,%d,%d”, chan_i, all_chans_b, on_b );

    SendData(s);
    read_data(status_c, IN_MSG_LEN, num_chars_read);
    }
    else if (item==’x’ || item==’X’ )
    {
    quit = true;
    }
    }

    tidyup();
    FreeConsole();

    return 0;
    }

    void SelectWinSock1_1()
    {
    // select WinSock version 1.1
    WORD version = MAKEWORD(1, 1);
    WSADATA wsaData;

    error = WSAStartup(version, &wsaData);
    if (error != 0 )
    {
    printf (“WinSock not available”);
    exit(1);
    }

    if (LOBYTE(wsaData.wVersion) != 1 ||
    HIBYTE(wsaData.wVersion) != 1 )
    {
    printf (“WinSock 1.1 not available”);
    WSACleanup();
    exit(2);
    }
    }

    void CreateSocket()
    {
    // create kernel data structures

    theSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (theSocket == INVALID_SOCKET)
    printf(“Can’t create socket\n”);
    }

    bool ConnectToServer()
    {
    bool connected = true;

    // Note: We use a hard-wired port number,
    // this is not strictly proper but should be ok
    // for all practical purposes.

    // server is “localhost” on port 15650
    theServerAddress.sin_family = AF_INET;
    theServerAddress.sin_port = htons(15650);

    // EITHER:

    // find IP address of STR4500_rem_pr application. If this is on the same
    // PC as this program then set the hostname to “localhost”
    // If on a different PC then set the network name of the other pc,
    // e.g. the Spirent lab HP vectra PC is “paulc-310”
    HOSTENT* pHost = gethostbyname(“localhost”);
    in_addr* pHexAddress = (in_addr*) pHost->h_addr_list[0];
    char* DottedAddress = inet_ntoa(*pHexAddress);
    theServerAddress.sin_addr.s_addr = inet_addr(DottedAddress);

    // OR:

    // Alternatively to connect to a STR4500_rem_pr application on a remote system
    // where you know the dotted IP address delete the above and use this:

    // char DottedAddress[20] = “10.24.1.12”; // (for example)
    // theServerAddress.sin_addr.s_addr = inet_addr(DottedAddress);

    // END EITHER/OR

    error = connect(theSocket,
    (const SOCKADDR*) &theServerAddress,
    sizeof(SOCKADDR_IN));
    if (error == SOCKET_ERROR)
    {
    printf(“Can’t connect to server\n”);
    connected = false;
    }

    return connected;
    }

    void SendData(char *message)
    {
    // send the message
    error = send(theSocket,
    message,
    strlen(message),
    0);
    if (error == SOCKET_ERROR) printf(“Can’t send data\n”);
    }

    void read_data ( char *data, int max_chars, int &num_chars_read )
    {
    num_chars_read = recv(theSocket, data, max_chars, 0);
    if(num_chars_read == SOCKET_ERROR)
    {
    int error_code = WSAGetLastError();
    printf (“Error in Connection::read_data() error code %d”, error_code);
    }
    }

    // Solicit status message, extract status enum from reply and return it
    // Note additional data is returned in top bits of status byte but these
    // are not handled here
    int get_status()
    {
    char data[IN_MSG_LEN +1];
    int num_chars_read;
    int status;

    SendData(“NULL”);
    read_data(data,IN_MSG_LEN, num_chars_read);
    // message is “Status XX(,last fatal error message)
    sscanf(&data[7],”%x”,&status);

    // temporarily print any fatal error message here.
    // ***** Will need to return them and handle them in calling prog. in a real app. ********
    if(num_chars_read > 12)
    {
    // null terminate string, then print it
    data[num_chars_read] = ”;
    printf(“%s\n”, data);
    }
    return (status & 0xf); // for now don’t handle higher bits
    }

    void tidyup()
    {
    closesocket(theSocket);
    printf(“closing client\n”);
    }

Leave a comment