Semi-Object Oriented C Development – Part 4

An Alternate Way…

I haven’t added to this series in a while, but a friend of mine named Cole left a comment on my first post that I thought I should share. He recommended an alternative implementation for typedefing your “C” object.

For those of you that remember from part 1, I typedef’d my C object as a void pointer…

typedef void *Vector2;

This works well, but, as Cole points out, it is horrible for debugging because you cannot see what the members of your C object are while in the debugger. This would still be the preferred choice if your library is closed source and a debug version will not be available. However, if you are developing an open source project or internal company code there is a better option that works with the debugger.

Cole suggests you forward type your struct as follows:

struct _Vector2;
typedef struct _Vector2 *Vector2;

Then, in your C code you would define things as such:

struct _Vector2 {
     float x;
     float y;
};

Vector2 vector2_Create()
{
     Vector2 vector2 = (Vector2)malloc(sizeof(struct _Vector2));

     if (vector2 == NULL)
          return NULL;

     vector2->x = 0.0f;
     vector2->y = 0.0f;

     return vector2;
}

void vector2_Destroy(Vector2 this)
{
	if (this != NULL)
		free (this);
}

/* and so on... */

For the skeptic, I tested this and it is perfectly acceptable. It works wonderfully, thank you for this one Cole :).

Some Other Tricks…

Type Casting

If you also remember, I suggested you use a C preprocessor macro to type cast your C object. Something like this:

#define _TVECTOR2(this) ((_Vector2 *)(this))

I have since abandoned this in favor of type casting when you receive the variable. If you remember the Normalize “method” from part 2

/* in the .H */
void vector2_Normalize(Vector2 this);

/* in the .C */
void vector2_Normalize(Vector2 this)
{
     _Vector2 *vec2 = ((_Vector2 *)this);
     double sizeSq = 0.0;
     float scaleFactor = 0.0f;

     assert(this != NULL); /* fyi assert.h */

     sizeSq = (vec2->x * vec2->x) + (vec2->y * vec2->y);
     if (sizeSq < 10e-20) return;

     scaleFactor = 1.0f/(float)sqrt(sizeSq);

     vec2->x *= scaleFactor;
     vec2->>y *= scaleFactor;

     return;
}

I type cast “this” as a (_Vector2 *). This can be done automatically by typing it as _Vector2 * in the parameters (only in the .C file) as such:

/* in the .H */
void vector2_Normalize(Vector2 this);

/* in the .C */
void vector2_Normalize(_Vector2 *this)
{
     double sizeSq = 0.0;
     float scaleFactor = 0.0f;

     assert(this != NULL); /* fyi assert.h */

     sizeSq = (this->x * this->x) + (this->y * this->y);
     if (sizeSq < 10e-20) return;

     scaleFactor = 1.0f/(float)sqrt(sizeSq);

     this->x *= scaleFactor;
     this->y *= scaleFactor;

     return;
}

This is easier than using the _TVECTOR2 macro.

NOTE: If you use the method described in the “An Alternate Way…” above then you don’t need to type cast at all because the offsets will already be known to the compiler.

C++ Compiler

If you are using C++ to compile any of my C object examples, using “this” as the name of the C object will not work because it is a reserved word. I would suggest using “self” instead.

Character Limits

At my job I use a C compiler that only recognizes the first 31 characters of a subroutine name. For instance, if you have the following function:

void connectionObject_SetReconnectAttemptCount(ConnectionObject this, int count);
void connectionObject_SetReconnectAttemptDelay(ConnectionObject this, int delay);

The C compiler will ignore anything after 31 characters, so the routines above look like this to the C compiler:

void connectionObject_SetReconnectAt(ConnectionObject this, int count);
void connectionObject_SetReconnectAt(ConnectionObject this, int delay);

The problem here is obvious, these subroutines will map to eachother. This can be overcome by using the C preprocessor to shorten the subroutine names…

/* In the .H file */
#define connectionObject_SetReconnectAttemptCount connObj_SetRecAttCnt
void connectionObject_SetReconnectAttemptCount(ConnectionObject this, int count);

#define connectionObject_SetReconnectAttemptDelay connObj_SetRecAttDel
void connectionObject_SetReconnectAttemptDelay(ConnectionObject this, int delay);

The C preprocessor will replace the long over 31-character subroutine names with the shortened form you define. As long as connObj_SetRecAttCnt and connObj_SetRecAttDly are not present somewhere else in your libraries then you are golden!

Advertisements

Father, Husband, Software Developer, Podcaster, Blogger, Gamer, and the Future Leader of the Zombie Resistance. My thoughts are my own.

Tagged with: , , , ,
Posted in C, Coding, OOP

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

Error: Twitter did not respond. Please wait a few minutes and refresh this page.

%d bloggers like this: