• No results found

The Prototype Object System

In document 121936460-Learn-C-The-Hard-Way.pdf (Page 100-103)

The OOP system we’ll create is a simple "prototype" style object system more like JavaScript. Instead of classes, you start with prototypes that have fields set, and then use those as the basis of creating other object instances. This "classless" design is much easier to implement and work with than a traditional class based one.

20.2.1

The Object Header File

I want to put the data types and function declarations into a separate header file named object.h. This is standard C practice and it lets you ship binary libraries but still let the programmer compile against it. In this file I have several advanced CPP techniques I’m going to quickly describe and then have you see in action later:

object.h

1 #ifndef _object_h 2 #define _object_h 3

4 typedef enum {

5 NORTH, SOUTH, EAST, WEST 6 } Direction;

7

8 typedef struct {

9 char *description;

10 int (*init)(void *self); 11 void (*describe)(void *self); 12 void (*destroy)(void *self);

13 void *(*move)(void *self, Direction direction); 14 int (*attack)(void *self, int damage);

15 } Object; 16

17 int Object_init(void *self); 18 void Object_destroy(void *self); 19 void Object_describe(void *self);

20 void *Object_move(void *self, Direction direction); 21 int Object_attack(void *self, int damage);

22 void *Object_new(size_t size, Object proto, char *description); 23

24 #define NEW(T, N) Object_new(sizeof(T), T##Proto, N) 25 #define _(N) proto.N

26

27 #endif

Taking a look at this file, you can see we have a few new pieces of syntax you haven’t encountered before: #ifndef You’ve seen a #define for making simple constants, but the CPP can also do logic and remove sections

of code. This #ifndef is "if not defined" and checks if there’s already a #define _object_h and if there is it skips all of this code. I do this so that we can include this file any time we want and not worry about it defining things multiple times.

#define With the above #ifndef shielding this file from we then add the _object_h define so that any attempts to include it later cause the above to skip.

#define NEW(T,N) This makes a macro, and it works like a template function that spits out the code on the right, whenever you write use the macro on the left. This one is simply making a short version of the normal way we’ll call Object_new and avoids potential errors with calling it wrong. The way the macro works is the T and N parameters to NEW are "injected" into the line of code on the right. The syntax T##Proto says

20.2. THE PROTOTYPE OBJECT SYSTEM 89

to "concat Proto at the end of T", so if you had NEW(Room, "Hello.") then it’d make RoomProto there.

#define _(N) This macro is a bit of "syntactic sugar" for the object system and basically helps you write obj->proto.blah as simply obj->_(blah). It’s not necessary, but it’s a fun little trick that I’ll use later.

20.2.2

The Object Source File

The object.h file is declaring functions and data types that are defined (created) in the object.c, so that’s next:

object.c

1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include "object.h" 5 #include <assert.h> 6

7 void Object_destroy(void *self) 8 {

9 Object *obj = self; 10 11 if(obj) { 12 if(obj->description) free(obj->description); 13 free(obj); 14 } 15 } 16

17 void Object_describe(void *self) 18 {

19 Object *obj = self;

20 printf("%s.\n", obj->description); 21 }

22

23 int Object_init(void *self) 24 {

25 // do nothing really 26 return 1;

27 } 28

29 void *Object_move(void *self, Direction direction) 30 {

31 printf("You can't go that direction.\n"); 32 return NULL;

33 } 34

35 int Object_attack(void *self, int damage) 36 {

37 printf("You can't attack that.\n"); 38 return 0;

39 } 40

41 void *Object_new(size_t size, Object proto, char *description) 42 {

43 // setup the default functions in case they aren't set 44 if(!proto.init) proto.init = Object_init;

90 CHAPTER 20. EXERCISE 19: A SIMPLE OBJECT SYSTEM

45 if(!proto.describe) proto.describe = Object_describe; 46 if(!proto.destroy) proto.destroy = Object_destroy; 47 if(!proto.attack) proto.attack = Object_attack; 48 if(!proto.move) proto.move = Object_move;

49

50 // this seems weird, but we can make a struct of one size, 51 // then point a different pointer at it to "cast" it

52 Object *el = calloc(1, size); 53 *el = proto;

54

55 // copy the description over

56 el->description = strdup(description); 57

58 // initialize it with whatever init we were given 59 if(!el->init(el)) {

60 // looks like it didn't initialize properly 61 el->destroy(el);

62 return NULL;

63 } else {

64 // all done, we made an object of any type

65 return el;

66 } 67 }

There’s really nothing new in this file, except one tiny little trick. The function Object_new uses an aspect of how structs work by putting the base prototype at the beginning of the struct. When you look at the ex19.h header later, you’ll see how I make the first field in the struct an Object. Since C puts the fields in a struct in order, and since a pointer just points at a chunk of memory, I can "cast" a pointer to anything I want. In this case, even though I’m taking a potentially larger block of memory from calloc, I’m using a Object pointer to work with it.

I explain this a bit better when we write the ex19.h file since it’s easier to understand when you see it being used.

That creates your base object system, but you’ll need a way to compile it and link it into your ex19.c file to create a complete program. The object.c file on its own doesn’t have a main so it isn’t enough to make a full program. Here’s a Makefile that will do this based on the one you’ve been using:

The Makefile

1 CFLAGS=-Wall -g 2 3 all: ex19 4 5 ex19: object.o 6 7 clean: 8 rm -f ex19

This Makefile is doing nothing more than saying that ex19 depends on object.o. Remember how make knows how to build different kinds of files by their extensions? Doing this tells make the following:

1. When I say run make the default all should just build ex19.

In document 121936460-Learn-C-The-Hard-Way.pdf (Page 100-103)