2

I have files Record.h and Record.cpp. When I just include the Record.h file, I get several undefined reference errors to functions defined in those files. When I also include Record.cpp then the errors go away. Why is that? Record.h has the forward declarations for the functions it says are an undefined reference.

Record.h

#ifndef RECORD_
#define RECORD_

#include <string>
#include <limits>
using namespace std;

#define PKEYSIZE 10
#define STREETNUMSIZE 8
#define STREETNAMESIZE 24
#define RNAMESIZE 30
#define ASTYLESIZE 20
#define YEARSIZE 12

#define MAXBUCKETSIZE 4
#define MAXDIRECTORYSIZE 100

typedef struct
{

        char streetNum[STREETNUMSIZE];
        char streetName[STREETNAMESIZE];
        char rName[RNAMESIZE];
        char aStyle[ASTYLESIZE];
        char year[YEARSIZE];
        char pkey[PKEYSIZE];

} RECORD;

typedef struct
{
    int size;
    int depth;
    RECORD record[MAXBUCKETSIZE];

} BUCKET;


#define HEADERSIZE 2L

#define NODESIZE sizeof(BUCKET)

void addKey(RECORD *rec_ptr, string key);
void addStreetNum(RECORD *rec_ptr, string s);
void addStreetName(RECORD *rec_ptr, string s);
void addRName(RECORD *rec_ptr, string s);
void addAStyle(RECORD *rec_ptr, string s);
void addYear(RECORD *rec_ptr, string s);
void printRecord(RECORD *rec_ptr);
ostream & operator<<(ostream & out, RECORD *r);

string cvt_binary(unsigned int input);
void addRecord(RECORD *r);
void showbucket(int n);

#endif

Record.cpp

int dirDepth = 0;
int numberOfBuckets = 0;
int directory[MAXDIRECTORYSIZE];
BUCKET bucket[MAXDIRECTORYSIZE/MAXBUCKETSIZE];

void addKey(RECORD *rec_ptr, string key)
{
    strncpy(rec_ptr->pkey, key.c_str(), PKEYSIZE);

}

void addStreetNum(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->streetNum, s.c_str(), STREETNUMSIZE);
}

void addStreetName(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->streetName, s.c_str(), STREETNAMESIZE);
}

void addRName(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->rName, s.c_str(), RNAMESIZE);
}

void addAStyle(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->aStyle, s.c_str(), ASTYLESIZE);
}


void addYear(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->year, s.c_str(), YEARSIZE);
}

void printRecord(RECORD *rec_ptr)
{

    cout<< "|"
        << rec_ptr->pkey << "|" 
        << rec_ptr->streetNum << "|" 
        << rec_ptr->streetName << "|" 
        << rec_ptr->rName << "|" 
        << rec_ptr->aStyle << "|" 
        << rec_ptr->year << endl;

}


ostream & operator<<(ostream & out, RECORD *r)
{
    out << r->pkey << r->streetNum << r->streetName << r->rName
        << r->aStyle << r->year << endl;
    return out;

}

int bucketread(short rrn, BUCKET *page_ptr)
{
//  long lseek(), addr;
    long addr;

    addr = (long)rrn * (long)NODESIZE + HEADERSIZE;
    lseek(btfd, addr, 0);
    return ( read(btfd, page_ptr, NODESIZE) );
}

int bucketwrite(short rrn, BUCKET *page_ptr)
{
//    long lseek(), addr;
    long addr;
    addr = (long) rrn * (long) NODESIZE + HEADERSIZE;
    lseek(btfd, addr, 0);
    return (write(btfd, page_ptr, NODESIZE));
}

void showbucket(int n)
{
    cout << "loading bucket " << n << endl;
    BUCKET b;
    bucketread(n, &b);
    cout << "there are " << b.size << " records in the bucket" << endl;

}

string cvt_binary(unsigned int input) {
    if(input == 0) return "0"; // trivial case
    string result;
    for(int i = numeric_limits<unsigned int>::digits - 1; i >= 0; --i) {
        if(input & (1 << i)) 
        {
            result += "1";
        } 
        else 
        {
            if(!result.empty()) result += "0";
        }
    }
    return result;
}


string hash (char* key)
{
    int sum = 0;
    int len = strlen(key);
    if (len % 2 == 1) len++; // make len even
    //for an odd length string, use the trailing 0 as part of key
    for (int j = 0; j < len; j +=2)
        sum = (sum + 100 * key[j] + key[j+1]) % 19937;
    return cvt_binary(sum);
}

void copyrecord(RECORD *dest, RECORD *src)
{
    cout << "copying record" << endl;
    addKey(dest, src->pkey);
    addStreetNum(dest, src->streetNum);
    addStreetName(dest, src->streetName);
    addRName(dest, src->rName);
    addAStyle(dest, src->aStyle);
    addYear(dest, src->year);
}

void addToBucket(int n, RECORD *r)
{
    cout << "Adding record " << r->pkey << " to bucket " << n << endl;
    if (bucket[n].size == MAXBUCKETSIZE)
    {
        cout << "Bucket " << n << " is full." << endl;
        // examine bucket depth and directory depth to determine next action
        cout << "Bucket depth: " << bucket[n].depth << endl;
        cout << "Directory depth: " << directory[0] << endl;
    }
    else
    {
        copyrecord(&bucket[n].record[bucket[n].size],r);
        bucket[n].size++;
        bucketwrite(1,&bucket[1]);


    }

}

string getreverse(string key, int num)
{
    if(num==0)
        return "";
    string newstring;
    newstring = key.at(key.length());
    newstring+= getreverse(key.substr(0,key.length()-1),num-1);

    return newstring;
}

void addRecord(RECORD *r)
{
    cout << r->pkey << endl;
    string hashvalue = hash(r->pkey);
    cout << "hash value is " << hashvalue << endl;

    int directoryDepth = directory[0];

    if(directoryDepth == 0)
    {
        directory[1] = 1;
        addToBucket(1, r);
    }
    else
    {
        // use hashing to figure out which bucket to add to
        cout << "The relevant string is" <<  getreverse(hashvalue, directoryDepth) << endl;

    }

}
Donal Fellows
  • 120,022
  • 18
  • 134
  • 199
neuromancer
  • 47,047
  • 74
  • 161
  • 217

3 Answers3

3

Record.h has the declarations of those functions. Record.cpp has the definitions.

//Record.h

void foo(); // This is a forward declaration

So far so good.

//main.cpp

#include "Record.h"

int main()
{
    foo();
}

Now main.o will compile just fine. But if you try to link it into a working binary, you'll get a complaint that void foo() is undefined.

//Record.cpp

#include "Record.h"

// This is the definition
void foo()
{
    // do various things
}

This can be compiled into Record.o, which can then be linked with main.o into a working executable.

Beta
  • 86,746
  • 10
  • 132
  • 141
  • `void foo();` is a _declaration_. The "forward" is meaningless, there is no other. It's `main.cpp` what is compiling fine, not `main.o`. If you fix these, I'd feel better about my up-vote. – sbi May 23 '10 at 07:26
  • @sbi: Since `void foo();` is a declaration of something that has not yet been defined (as far as main is concerned) it is by definition a forward declaration-- whether there are others doesn't matter. Actually it is g++ that is compiling (or maybe Phenom), and I have heard both the source file (main.cpp) and the object file (main.o) used as the subject of the ergative form of the verb-- I was going for clarity, but being a grammar nazi myself I would be glad to hear a well-reasoned argument for one over the other. – Beta May 23 '10 at 07:46
  • @Beta, re declarations that are or are not _forward_ declarations: What's a relevant practical case of a declaration __not__ being a _forward_ declaration? (Why declare something that's already defined, when, by definition, a definition is a declaration, too?) As for what a compiler compiles: My dish washer washes dirty dishes and produces clean ones. Likewise, my compiler compiles source files and produces object files. – sbi May 23 '10 at 14:22
  • @sbi: Declarations: I respectfully suggest that you have lost track of the point you are trying to make. Dishes: not a strong argument since 1) you are using "wash" as a transitive verb which makes it irrelevant, 2) we don't say "the dishes wash", and 3) one example doesn't make a case: we say "the bread is baking", not "the dough is baking". – Beta May 23 '10 at 19:58
  • @Beta: 1) "Forward" implies that it is declared _before-hand_, that is, _before the definition_. But what else are declarations for? If they appear _after_ the definition, they are _redeclarations_ (as every definition is a declaration, too). Usually declarations are used to declare something _before_ it is defined. They are all either _forward declarations_ or _redeclarations_. (BTW, the term _forward_ declaration I have only ever heard for classes, [where it is indeed meaningless, as there are no other forms of class declarations](http://stackoverflow.com/questions/1410563/1410632#1410632).) – sbi May 23 '10 at 21:38
  • @Beta: 2) What I meant to say is that the act of processing usually refers to the input as what is processed upon (sorry if that sounds awkward, I'm not a native), but you have found a good counter example. Your answer is the very first time I ever heard that a compiler compiles object files, but, thinking about it, this might be because I'm a non-native, and have never thought about the verb's meaning outside of translating a program. So I retract and say that, yes, while I have never heard it, it might very well make sense to say it that way, I just wouldn't know. I'm sorry for that. – sbi May 23 '10 at 21:45
  • @sbi: I think we actually agree about declarations: my use of "forward" was technically correct but unneeded. As for "compile", I can think of examples on both sides but I can see no rule. I see that you are from Germany-- German is a more precise language than English, and maybe you have a clear rule for this, and maybe an English grammarian would say that "the source compiles" is correct, but I have heard both forms so I use whichever one seems to make things most clear at the time. (P.S. I meant no offense when I called myself a grammar nazi!) – Beta May 24 '10 at 01:24
3

Providing the declarations by including the header is what the compiler needs. The linker needs the definitions. It will find these in the object files the compiler creates when it compiles source files.
I'm not using gcc, but I think it can just be called with all the source files and will then call the linker with all the object files provided: g++ main.cpp record.cpp.

sbi
  • 204,536
  • 44
  • 236
  • 426
0

My C++ might be a bit rusty, but it sounds like Record.cpp isn't being compiled into the solution. If the .cpp file isn't there, then the header has nothing to refer to. Don't include the cpp, but check your linker settings, or the command you're using to compile your program... it needs to include all the cpps.

mpen
  • 237,624
  • 230
  • 766
  • 1,119