Some Obscure C++

& other useful stuff


 

 

Structs and classes are the same

 

 

 

Anonymous Structures

In C++, you can define anonymous structures, unions and enumerations within other classes and structures. I.e. :

 


class CVec3 {
public:

  struct {
  float x;
float y;
float z;
float w;
} data;

};

CVec3 vec;
vec.data.x = 0.0f;


 

 

The structure instance, data, is unique in type and exists as a variable within any instance of the CVec3 class. To access the data we have to use statements such as vec.data.x . This example isn't particularly useful, however you can create an anonymous struct without specifying an instance :

 

 


class CVec3 {
public:

  struct {
  float x;
float y;
float z;
float w;
};

};



In this situation the contents of the structure become part of the parent scop. i.e,


class CVec3 {
public:

  struct {
  float x;
float y;
float z;
float w;
};

};

CVec3 vec;
vec.x = 0.0f;


 

Ok, so the above has no real practical use. The example below does however...

 


class CVec3 {
public:

 

union {

 

float data[4];

struct {

  float x;
float y;
float z;
float w;
};

};

};

CVec3 vec;
vec.x = 0.0f;
vec.data[0] = 0.0f;


 

The class CVec3 now has two ways of accessing the data. Either by array or individual component. The size of the class CVec3 remains the same and there is no difference in speed between using either form. It merely provides a slightly nicer interface.

 

 

 

Explicit Casts In C

 

In C you could force a variable to convert it’s data type using explicit casts :

 


int a = 2, b = 3;
float f = (float) a/b;



This has a large number of valid uses such as converting pointers to data types, elimination of rounding errors etc. It is also possible to cause massive bugs and errors that can be hard to fix (especially when you are introduced to polymorphic classes and virtual functions).

 

When the specification for C++ was being decided, one area that recieved revision was that of casting. The descision was made that casting should remain, but that it should be segregated into different types of casts that promote correct usage of casting operators.

 

 

static_cast< TYPE >( arg )

 

The static cast performs a static compile time type conversion. This will only take place if the variable (or expression) specified as the argument can be legally converted to TYPE. If the argument cannot be converted, then an error will be generated when trying to compile the code. Generally this type of cast is used for performing casts that you know are valid, for example :

 


int a = 2, b = 3;
float f = static_cast<float>( a/b );

or

int someArray;
void *ptr = static_cast<void*>( &someArray );


 

 

 

const_cast< TYPE >( arg )

 

The const cast allows you to convert a constant variable to a non-constant version. The const cast is generally used within a constant member function when a constant variable or function has to be accessed in a non-const manner. For example, the class fred contains an array, memberArray. Within the member function func, we want to assign a pointer to memberArray so that we can use that to iterate the array :

 


struct fred {

 

int memberArray[10];

void func() const
{
  int *ptr = memberArray; // memberArray is const
for(; ptr != (memberArray+10); ++ptr )
std::cout << *ptr << std::endl;
}

};



trying to compile this code will give you the following error:

 

main.cpp(20) : error C2440: 'initializing' : cannot convert from 'const int [10]' to 'int *'

 

The problem here, is that func has been declared as a constant function. This means that all member variables are const within this function. The compiler therefore see's this line :

 


int *ptr = memberArray;


 

and decides that you are trying to access the constant data in a way that would allow you to change it. It therefore prevents you from doing it. The function above however, uses ptr in a safe manner. At no point, is it used to change the data. It is only used for reading the array variables, therefore we can use const_cast to get this to compile and work. We can re-write the above code to give us :

 


struct fred {

 

int memberArray[10];

void func() const
{
  int *ptr = const_cast<int*>(memberArray);
for(; ptr != (memberArray+10); ++ptr )
std::cout << *ptr << std::endl;
}

};


 

 

dynamic_cast< TYPE >( arg )

 

Dynamic casts allow for the runtime conversion of data types. This cast only works on polymorhpic types (those with one or more virtual function) . This cast uses what is known as Run-Time-Type-Information (RTTI). I will say no more though, until you do inheritance fully....

 

 

reinterpret_cast< TYPE >( arg )

 

The above three casts should be the only types of cast that you should ever need within C++. Originally the specification was drawn up to only include the above casts however, in order to fully replace C's casting mechanism, C++ must also impliment a way to perform unsafe casts (?). This rather bizarre language feature can therefore be used in such interesting and well coded ways as this !

 


// some complex data type
std
::vector< std::string > array;

// perform an impossible type conversion
char
c = reinterpret_cast< char >( array );


 

I don't recommend using it !

 

 

Copy Constructor's

 

Generally it is recommended that you always define a copy constructor for your class. This is needed whenever you need to copy an instance of a class, for example, functions that either accept arguments that are call by value, or return an entire instance (ie, some of the binary operators that must return temporary variables).

 

A copy constructor is merely a constructor function that takes a constant reference to a class of it's type. For example :

 


class CVec3 {
public:

 

// default constructor
CVec3
() : x(0),y(0),z(0),w(1) {}

// copy constructor
CVec3
(const CVec3& v)
{

  x = v.x;
y = v.y;
z = v.z;
w = v.w;

}

// constructor for 4 element float array
CVec3
(const float v[])
{

  x = v[0];
y = v[1];
z = v[2];
w = v[3];

}

// constructor for floats

CVec3( const float x_,
const float y_,
const float z_,
const float w_ = 1.0f)
: x(x_), y(y_), z(z_), w(w_){}


// ---- Data ----

union {

 

float data[4];

struct {

  float x;
float y;
float z;
float w;
};

};

};


 

It is also worth mentioning that static_cast will fail unless a copy constructor is provided for the cast type.

 

 

 

Checking for MSB, LSB

 

Ahhh! Remember those good old PCS/OST set of lectures? Remember the differences between least / most significant bytes? Essentually for a two byte variable, where ther bytes are AB, you could write that as AB or BA depending on which way the system interpreted bytes.

 

We need to be aware of this difference when reading and writing binary files. So we will need a way to easily check the byte ordering. This can be done quite nicely with a union,

 


union ShortToBytes {

  unsigned short s;
unsigned char bytes[2];

};

/*
* This function is used to determine if the system has
* LSB or MSB byte ordering. By using a union, both the
* 2byte variable(short) and the array of 2 individual
* bytes (chars) will be stored in the same memory
* location. ie, the ShortToBytes
union data type is
* only two bytes in size and allows you to split apart
* a 2byte variable to check the ordering.
*/

int CheckByteOrder()
{

  // Create a 2byte check variable
ShortToBytes check;
// assign 1 to the 2byte variable
check.s = 1;
// check the first byte
if(check.bytes[0] == 1)
  return 1;

return 0;

}


 

If at some point you require to swap some bytes about, you can do it using functions a bit similar to this :

 


void Swap2Bytes( void *ptr )
{

 

unsigned char *pchar,temp;

// cast pointer to a useful one
pchar = static_cast<unsigned char*>(ptr);

// swap the bytes around
temp = pchar[0];
pchar[0] = pchar[1];
pchar[1] = temp;

}

void Swap4Bytes( void *ptr )
{

 

unsigned char *pchar,temp;

// cast pointer to a useful one
pchar = static_cast<unsigned char*>(ptr);

// swap the outer bytes around
temp = pchar[0];
pchar[0] = pchar[3];
pchar[3] = temp;

// swap the outer bytes around
temp = pchar[1];
pchar[1] = pchar[2];
pchar[2] = temp;

}


 

 

 

Structure alignment and packing

 

Most 32bit processors tend to be optimised for reading memory in chunks of 4 bytes. Most compilers (Visual C++ included) will automatically align structure member data so that it is aligned to 4bytes to speed up data reads and writes. When writing certain structures to represent the layout of binary files, this automatic alignment can mess up your reading of structures.

 

The following code demonstrates how to tell the compiler to align structures to single bytes. The tga_header structure between the two #pragma pack statements is now aligned to single bytes, ie the compiler has not added any padding. This means that if you were to now call sizeof(tga_header) it would return 18. Without the #pragma's sizeof(tga_header) would return 20.

 


#pragma pack(push,1)


/*
* This structure is used to hold the header
* of the tga image file format
*/

struct tga_header
{

 

// size of image identification field
unsigned char m_ImageIdent;
// 0 - No colour map, 1 - has colour map

unsigned char m_ColourMapType;
// 0 - No Image, 1 - 8bit, 2 - uncompress RGB .....

unsigned char m_ImageType;


// Index of first colour map entry

unsigned short m_ColourMapOrigin;
// Number of colour map entries

unsigned short m_ColourMapSize;


// Number of bits for each entry

unsigned char m_ColourMapESize;
// x origin of image

unsigned short m_Xorigin;


// y origin of image

unsigned short m_Yorigin;
// width of image in pixels

unsigned short m_Width;


// height of image in pixels

unsigned short m_Height;
// number of bits stored in each pixel index

unsigned char m_PixelIndexSize;
// should always be zero....

unsigned char m_ImageDescByte;

};


#pragma pack(pop)


 

All of this allows you to read the header of a tga file with a simple :

 


// open file
FILE *fp = fopen("filename.tga","rb");

// check file opened ok
if(fp) {

  tga_header header;

// read the header in one go
fread(&header,1,sizeof(tga_header),fp);

// ...

}
fclose(fp);


 

 

 

Macro Tips

 

One of the aims of C++ was to replace some of the functionality provided by macro's and constants with versions that offered better type and compile time checking. So far we have seen inline functions that are a safer alternative to macros, the const keyword instead of the old #define size 10 stuff, and the final part is templates.

The pre-processor can still be extremely useful :

 

 

std::cout<< "file: " << __FILE__ << std::endl

  << "line: " << __LINE__ << std::endl;

 

 

 

 

inline void AssertInline( bool condition ) {

  if(! condition ) {
 

std::cerr<< "file: "<<__FILE__ <<std::endl

  << "line: "<<__LINE__<<std::endl;

exit(0);

}

}

#define AssertMacro( condition ) { \

  if(! (condition) ) { \
 

std::cerr<< "file: "<<__FILE__ <<std::endl \

  << "line: "<<__LINE__ <<std::endl;\

exit(0); \

} \

}


 

 

 

#ifdef WIN32

#include <windows.h>

#else

#include <irix_stuff.h>

#endif

 

 

 

 

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

 

 

 

 

#ifdef _DEBUG

// do some debug checks, error logging, etc

#endif

 

 

#ifdef _DEBUG

#define LOG(X) \

std::cerr<< "file: "<<__FILE__ <<std::endl \

  << "line: "<<__LINE__ <<std::endl \
<< "\t" << X << std::endl;

#define ASSERT( condition ) { \

  if(! (condition) ) { \
 

LOG ("Assertion failed");

exit(0); \

} \

}

#else

#define ASSERT( condition )

#define LOG( X )

#endif


 

 

 

 

Writing Header Files

 

 

#ifndef _MY_DEFINE_H_
#define _MY_DEFINE_H_

// externs and class definitions

#endif

 

 

Using C Header Files in C++

 

 

 

#ifdef __cplusplus
extern "C" {
#endif

  // C function prototypes

#ifdef __cplusplus
}
#endif