Flylib.com

Books Software

 
 
 

28.4 Turning Structures into Classes

I l @ ve RuBoard

28.4 Turning Structures into Classes

Frequently when examining C code you may find a number of defined struct statements that look like they should be objects defined as C++ class es. Actually, a structure is really just a data-only class with all the members public.

C programmers frequently take advantage of the fact that a structure contains only data. One example of this is reading and writing a structure to a binary file. For example:

a_struct struct_var;	   // A structure variable

// Perform a raw read to read in the structure
read_size = read(fd, (char *)&struct_var, sizeof(struct_var));

// Perform a raw write to send the data to a file
write_size = write(fd, (char *)&struct_var, sizeof(struct_var));

Turning a structure like this into a class can cause problems. C++ keeps extra information, such as virtual function pointers, in a class. When you write the class to disk using a raw write, you are outputting all that information. What's worse , when you read the class in, you overwrite this bookkeeping data.

For example, suppose we have the class:

class sample {
    public:
        const int sample_size;     // Number of samples
        int cur_sample;            // Current sample number
        sample(  ) : sample_size(100) {} // Set up class
        virtual void get_sample(  ); // Routine to get a sample
};

Internally, this class consists of three member variables : a constant, sample_size (which C++ won't allow you to change); a simple variable, cur_sample ; and a pointer to the real function to be used when get_sample is called. All three of these are written to disk by the call:

sample a_sample;
// ...
write_size = write(fd, (char *)&a_sample, sizeof(a_sample));

When this class is read, all three members are changed. That includes the constant (which we aren't supposed to change) and the function pointer (which now probably points to something strange ).

C programmers also make use of the memset function to set all the members of a structure to zero. For example:

struct a_struct { ... }
a_struct struct_var;
// ...
memset(&struct_var, '
struct a_struct { ... } a_struct struct_var; // ... memset(&struct_var, '\0', sizeof(struct_var));
', sizeof(struct_var));

Be careful when turning a structure into a class. If we had used the class a_sample in the previous example instead of the structure struct_var , we would have zeroed the constant sample_size as well as the virtual function pointer. The result would probably be a crash if we ever tried to call get_sample.

I l @ ve RuBoard
I l @ ve RuBoard

28.5 setjmp and longjmp

C has its own way of handling exceptions through the use of setjmp and longjmp . The setjmp function marks a place in a program. The longjmp function jumps to the place marked by setjmp .

Normally setjmp returns a zero. This tells the program to execute normal code. When an exception occurs, the longjmp call returns to the location of the setjmp function. The only difference the program can see between a real setjmp call and a fake setjmp call caused by a longjmp is that normally setjmp returns a zero. When setjmp is "called" by longjmp , the return value is controlled by a parameter to longjmp.

The definition of the setjmp function is:

#include <setjmp.h>

int setjmp(jmp_buf env);

where env is the place where setjmp saves the current environment for later use by longjmp .

The setjmp function return values are as follows :

Normal call

Nonzero

Non-zero return codes are the result of a longjmp call.

The definition of the longjmp call is:

void longjmp(jmp_buf env, int return_code);

where env is the environment initialized by a previous setjmp call, and return_code is the return code that will be returned by the setjmp call.

Figure 28-1 illustrates the control flow when using setjmp and longjmp .

Figure 28-1. setjmp/longjmp control flow
figs/c++2_2801.gif

There is one problem here, however. The longjmp call returns control to the corresponding setjmp . It does not call the destructors of any classes that are " destroyed " in the process .

In Figure 28-1 we can see that in the subroutine we define a class named a_list . Normally we would call the destructor for a_list at the end of the function or at a return statement. However, in this case we use longjmp to exit the function. Since longjmp is a C function, it knows nothing about classes and destructors and does not call the destructor for a_list . So we now have a situation where a variable has disappeared but the destructor has not been called. The technical name for this situation is a "foul-up."

When converting C to C++, change all setjmp / longjmp combinations into exceptions.

I l @ ve RuBoard