-3

I am stuck at this compilation error and I wonder how to debug the code. My program basically has the following files:

Makefile:

CFLAGS = -Wall -g
src = ex19.c

all: $(src)
    cc $(CFLAGS) -o ex19 $(src)

ex19: object.o

clean:
    rm -f ex19

object.h:

#ifndef _object_h
#define _object_h

typedef enum{
    NORTH, SOUTH, EAST, WEST
} Direction;

typedef struct {
    char *description;
    int (*init)(void *self);
    void (*describe)(void *self);
    void (*destroy)(void *self);
    void *(*move)(void *self, Direction direction);
    int (*attack)(void *self, int damage);
} Object;

int Object_init(void *self);
void Object_destroy(void *self);
void Object_describe(void *self);
void *Object_move(void *self, Direction direction);
int Object_attack(void *self, int damage);
void *Object_new(size_t size, Object proto, char *description);

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

#endif

object.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "object.h"
#include <assert.h>

void Object_destroy(void *self) {
    Object *obj = self;

    if(obj) {
        if(obj->description) free(obj->decription);
        free(obj);
    }
}

void Object_describe(void *self) {
    Object *obj = self;
    printf("%s.\n", obj->description);
}

int Object_init(void *self){
    // do nothing really
    return 1;
}

void *Object_move(void *sefl, Direction direction){
    printf("You can't go that direction.\n");
    return NULL;
}

int Object_attack(void *self, int damage){
    printf("You can't attack that.\n");
    return 0;
}

void *Object_new(size_t size, Object proto, char *description){
    // setup the default functions in case they aren't set
    if(!proto.init) proto.init = Object_init;
    if(!proto.describe) proto.describe = Object_describe;
    if(!proto.destroy) proto.destroy = Object_destroy;
    if(!proto.attack) proto.attack = Object_attack;
    if(!proto.move) proto.move = Object_move;

    // this seems weird, but we can make a struct of one size,
    // then point a different pointer at it to "cast" it
    Object *el = calloc(1, size);
    *el = proto;

    // copy the description over 
    el->description = strdup(description);

    // initialize it with whatever init we were given
    if(!el->init(el)){
        // looks like it didn't initialize properly
        el->destroy(el);
        return NULL;
    }
    else {
        //all done, we made an object of any type
        return el;
    }
}

ex19.h:

#ifndef __ex19_h
#define __ex19_h

#include "object.h"

struct Monster {
    Object proto;
    int hit_points;
};

typedef struct Monster Monster;

int Monster_attack(void *self, int damage);
int Monster_init(void *self);

struct Room{
    Object proto;

    Monster *bad_guy;

    struct Room *north;
    struct Room *south;
    struct Room *east;
    struct Room *west;
};

typedef struct Room Room;

void *Room_move(void *self, Direction direction);
int Room_attack(void *self, int damage);
int Room_init(void *self);

struct Map{
    Object proto;
    Room *start;
    Room *location;
};

typedef struct Map Map;

void *Map_move(void *self, Direction direction);
int Map_attack(void *self, int damage);
int Map_init(void *self);

#endif

ex19.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "ex19.h"

int Monster_attack(void *self, int damage){
    Monster *monster = self;
    printf("You attack %s!\n", monster->_(description));
    monster->hit_points -= damage;
    if(monster->hit_points > 0) {
        printf("It is still alive.\n");
        return 0;
    }
    else{
        printf("It is dead!\n");
        return 1;
    }
}

int Monster_init(void *self){
    Monster *monster = self;
    monster->hit_points = 10;
    return 1;
}

Object MonsterProto = {
    .init = Monster_init,
    .attack = Monster_attack
};

void *Room_move(void *self, Direction direction) {
    Room *room = self;
    Room *next = NULL;

    if(direction == NORTH && room->north) {
        printf("You go north, into:\n");
        next = room->north;
    }
    else if(direction == SOUTH && room->south){
        printf("You go south, into:\n");
        next = room->south;
    }
    else if(direction == EAST && room->east){
        printf("You go east, into:\n");
        next = room->east;
    }
    else if(direction == WEST && room->west){
        printf("You go west, into:\n");
        next = room->west;
    }
    else {
        printf("You can't go that direction.");
        next = NULL;
    }

    if(next){
        next->_(describe)(next);
    }
    return next;
}

int Room_attack(void *self, int damage){
    Room *room = self;
    Monster *monster = room->bad_guy;

    if(monster){
        monster->_(attack)(monster, damage);
        return 1;
    }
    else{
        printf("You flail in the air at nothing. Idiot.\n");
        return 0;
    }
}

Object RoomProto = {
    .move = Room_move,
    .attack = Room_attack
};

void *Map_move(void *self, Direction direction){
    Map *map = self;
    Room *location = map->location;
    Room *next = NULL;

    next = location->_(move)(location, direction);
    if(next) {
        map->location = next;
    }
    return next;
}

int Map_attack(void *self, int damage){
    Map* map = self;
    Room *location = map->location;
    return location->_(attack)(location, damage);
}

int Map_init(void *self){
    Map *map = self;

    //make some rooms for a small map
    Room *hall = NEW(Room, "The great hall");
    Room *throne = NEW(Room, "The throne room");
    Room *arena = NEW(Room, "The arena, with the minotaur");
    Room *kitchen = NEW(Room, "Kitchen, you have the knife now");

    // put the bad guy in the arena
    arena->bad_guy = NEW(Monster, "The evil minotaur");

    // setup the map rooms 
    hall->north = throne;

    throne->west = arena;
    throne->east = kitchen;
    throne->south = hall;

    arena->east = throne;
    kitchen->west = throne;

    //start the map and the character off in the hall 
    map->start = hall;
    map->location = hall;

    return 1;
}

Object MapProto = {
    .init = Map_init,
    .move = Map_move,
    .attack = Map_attack
};

int process_input(Map *game){
    printf("\n> ");
    char ch = getchar();
    getchar();  // eat enter;

    int damage = rand() % 4;
    switch(ch){
        case -1:
            printf("Giving up? You such.\n");
            return 0;
            break;
        case 'n':
            game->_(move)(game, NORTH);
            break;
        case 's':
            game->_(move)(game, SOUTH);
            break;
        case 'e':
            game->_(move)(game, EAST);
            break;
        case 'w':
            game->_(move)(game, WEST);
            break;
        case 'a':
            game->_(attack)(game, damage);
            break;
        case 'l':
            printf("You can go:\n");
            if(game->location->north) printf("NORTH\n");
            if(game->location->south) printf("SOUTH\n");
            if(game->location->east) printf("EAST\n");
            if(game->location->west) printf("WEST\n");
            break;
        default:
            printf("What?: %d\n", ch);
    }
    return 1;
}

int main(int argc, char *argv[]){
    //simple way to setup the randomness
    srand(time(NULL));

    //make our map to work with
    Map *game = NEW(Map, "The hall of the Minotaur.");
    printf("You enter the ");
    game->location->_(describe)(game->location);
    while(process_input(game)) {
    }
    return 0;
}

The compilation error:

cc -Wall -g -o ex19 ex19.c /tmp/cccuR81O.o: In function Map_init': ex19/ex19.c:105: undefined reference toObject_new' ex19/ex19.c:106: undefined reference to Object_new' ex19/ex19.c:107: undefined reference toObject_new' ex19/ex19.c:108: undefined reference to Object_new' ex19/ex19.c:111: undefined reference toObject_new' ex19/ex19.c:180: more undefined references to `Object_new' follow collect2: error: ld returned 1 exit status make: *** [all] Error 1

Update 1: lines with problems

105  Room *hall = NEW(Room, "The great hall");
106  Room *throne = NEW(Room, "The throne room");
107  Room *arena = NEW(Room, "The arena, with the minotaur");
108  Room *kitchen = NEW(Room, "Kitchen, you have the knife now");

111  arena->bad_guy = NEW(Monster, "The evil minotaur");
M.M
  • 138,810
  • 21
  • 208
  • 365
drdot
  • 3,215
  • 9
  • 46
  • 81

1 Answers1

2

The makefile needs to be updated to link object.o into the executable. Currently your link line is:

cc $(CFLAGS) -o ex19 $(src)

If you execute make (or make all) then the command will expand to:

cc -Wall -g -o ex19 ex19.c

which tries to generate the executable using only ex19.c.

The build target ex19 is never invoked because all does not have it as a dependency. But even if it did, or you manually write make ex19, that still will only make object.o, you do not have any commands anywhere that link ex19.o with object.o to make the executable.

You should change src = ex19.c to be src = ex19.c object.c , since those are both source files. You can take out the ex19: object.o line since it is redundant here. This makefile will compile and link all source files in one line, which is fine.

If you want to change your makefile to use separate invocations of the compiler for each .c file (and therefore a third invocation required for the linker) then you'd need to make a few changes, it's probably best to model your makefile off an existing one.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Thank you for the answer. This works! Could you add an example on chaging the makefile to use seperate invocations of the compiler for each .c file and then linker invocation? I am interested to learn that. – drdot Dec 22 '14 at 23:40
  • @dannycrane [see here](http://stackoverflow.com/a/2481307/1505939) for one example. You can automate it so that you just have to provide `$(src)` and all the other rules transform that list of c files, but it's good to understand the basics first – M.M Dec 22 '14 at 23:48