Jump to content
  • Advertisement
Sign in to follow this  

Help with linker error [VS 2005]

This topic is 4435 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm at wits end trying to sort out this bloody linker error. It only occurs when I include "cFileSys.h". I have no idea why the error appears, and I can't get rid of it. So if anyone could take a look, I'd be super greatful. You need to compile it with the DirectX SDK, so you just need to fix the library paths in the project options. The file (.rar | 2.5mb) can be found here. All extra files, aka the one .bmp needed, is included. I think thats all...So Thanks!!!!!

Share this post


Link to post
Share on other sites
Advertisement
cFileSys.h has function definitions within the header file. Thus, these are compiled into the object file for every source file which #includes cFileSys.h. When more than one source file does so, the functions become multiply defined.

The problem here is the fact that the definitions are in the header - they need to be moved out, eg into a cFileSys.cpp. You probably cannot do this with the template functions - I'm not sure what to do with these, but I think these need to be moved to within the class definition.

Share this post


Link to post
Share on other sites
Again, go ahead and behold the article. Specifically, the last three paragraphs before "Other Considerations".

Share this post


Link to post
Share on other sites
_Sigma, here are cFileSys.h and cFileSys.cpp for your viewing pleasure:

cFileSys.h
#ifndef CFILESYS_H
#define CFILESYS_H

/**
***************** FILE ACCESS SYSTEM ********************

Created by Chris Marsh 2006

The purpose of this class is to provide an easy and effective way of writing to and from files
by providing a wrapper class for the standard I/O functions.

This handles binary and textual reading and writing. Text reading always returns a string, up to a line
in length. Binary will always return the original data type. If you try to write anything but a string to
a file in FS_TEXT mode, you'll just end up with the character represented by that number.

Strange things happen if you write to a file in binary then read in text mode, or visa versa. I've tried to handle
some of the strangness, such as the string buffer failing to create. Anyways, just don't do it. =)

This is presented as is, and contains no warantees what so ever.

These files can be used anyway you want, but this MUST be present.

THANKS to the folks at GameDev.net who helped with portions of the code.
*/





#include <string>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <sstream>
using namespace std;

/* Note about open modes */
/*
FS_APPEND assumes we are opening the file to read and write(to the end of the file) to.
FS_CREATE assumes we are creating a file(overwrite any old file) to open and read from.
FS_OPEN_ONLY assumes we are only opening the file to read from. THe file must exist or a FS_READ_ERROR is raised
- This mode exists because one would have to open the file in mode FS_APPEND, which isn't intuitive.
If the file is opened in FS_CREATE, the file is overwritten, thus if we try to read without a prior
write, a FS_READ_ERROR is raised, as the file is empty.
- In this mode, you cannot write to the file, until you re-open the file, and specify the write type.
*/


#define FS_BINARY 0x0
#define FS_TEXT 0x1

#define FS_APPEND 0xA
#define FS_CREATE 0xB
#define FS_OPEN_ONLY 0xC

#define FS_SUCCESS 0x2
#define FS_WRITER_INIT_ERROR 0x3
#define FS_WRITE_ERROR 0x4
#define FS_READER_INIT_ERROR 0x5
#define FS_READ_ERROR 0x6
#define FS_UNKOWN_ERROR 0x7

#define FS_DELETE_FAIL 0xF
#define FS_DELETE_SUCCESS 0xE

#define FS_MEMORY_ALLOC_ERROR 0xFF

#define FS_FUNCTION_ERROR 0xEE

//type def the unsigned int data type
typedef unsigned int UINT;

//Main class

class cFileSys
{
public:
//Overloaded write methods, handles just the basic data types atm

template< class T > int write( T input );
//template<> int write<std::string>(std::string input);

//Overloaded read methods, handles just the bast data types atm

template<class T> int read(T &output);
// template<> int read<std::string>(std::string &output, UINT maxChars, char endChar);

//constructor. MUST take parameters. no default () constructor
cFileSys(char *fileName, //file name
int action, //Append to a file or create a new file
int mode);//Type of write to perform (Binary or textual)

//destructor. Closes down our files, and deletes our variables.
~cFileSys();

//Resets the file pointer to the start of the file
//Used after a write
void resetFilePtr();

//Creates a file by opening a new stream then closing it
void createFile(char *fName);

//deletes the currently open file, after releasing it
//int deleteFile();

//closes the open file
bool closeFile();

//Retrieves the last error in human readable form
string getLastError();

int openFile(char* fileName, int action, int mode);

int readLine(char *output, UINT maxChars, char endChar);

private:
//name of the currently open file
char *fName;

//our file stream
fstream *file;

//out stream
ostream *writer;

//in stream
istream *reader;

//what mode to open the file in
int writeMode;

//holds the last triggered error
int lastError;

//append or create the file.
int writeAction;

};

//write an int to file
//can only be used in binary mode

template< class T > int cFileSys::write( T input )
{
switch(writeMode)
{
case FS_BINARY:
{
//Check for an invalid writer
if(!writer)
{
lastError = FS_WRITER_INIT_ERROR;
return(FS_WRITER_INIT_ERROR);
}

//if we want to append, seek to the end of the file
if(writeAction == FS_APPEND)
writer->seekp(0 , ios::end);

//write the int
if(!writer->write((char*)(&input),sizeof(input)))
{
lastError = FS_WRITE_ERROR;
return(FS_WRITE_ERROR);
}

return(FS_SUCCESS);
break;
}
case FS_TEXT:
{
//Check for an invalid writer
if(!writer)
{
lastError = FS_WRITER_INIT_ERROR;
return(FS_WRITER_INIT_ERROR);
}

//if we want to append, seek to the end of the file
if(writeAction == FS_APPEND)
writer->seekp(0 , ios::end);

stringstream ss;
ss << input;
string temp = ss.str();

if(write(temp) != FS_SUCCESS)
{
lastError = FS_WRITE_ERROR;
return(FS_WRITE_ERROR);
}

return(FS_SUCCESS);
break;
}
default:
{
lastError = FS_UNKOWN_ERROR;
return(FS_UNKOWN_ERROR);
}
}
}

// To avoid specialization/general template "collision"
template<> int cFileSys::write< std::string >( std::string input );


template<class T> int cFileSys::read(T &output)
{
switch(writeMode)
{
case FS_BINARY:
{
if(!reader)
{
lastError = FS_READER_INIT_ERROR;
return(FS_READER_INIT_ERROR);
}


int temp;

// Read the int in
if(!reader->read((char*)(&temp),sizeof(int)))
{
lastError = FS_READ_ERROR;
return(FS_READ_ERROR);
}

output = temp;

return(FS_SUCCESS);
break;
}
case FS_TEXT:
{
lastError = FS_FUNCTION_ERROR;
return(FS_FUNCTION_ERROR);
break;
}
default:
{
lastError = FS_UNKOWN_ERROR;
return(FS_UNKOWN_ERROR);
}
}
}

// To avoid specialization/general template "collision"
template<> int cFileSys::read< std::string >( std::string &output );


#endif


cFileSys.cpp
#include "cFileSys.h"


// Constructor
//takes the file to load, write mode and type of write to perform
cFileSys::cFileSys(char* fileName, int action, int mode)
{
//set internal filename
fName = fileName;

//set the internal mode
writeMode = mode;


writeAction = action;

//if we want to create a new file, call the createfile function
if(action == FS_CREATE)
createFile(fileName);

//depending on what mode we want to open the file in,
//changes the parameters that are passed to the fstream constructor
switch(writeMode)
{
case FS_BINARY:
{
file = new fstream(fName, ios::in | ios::out | ios::binary);
break;
}

case FS_TEXT:
{
file = new fstream(fName, ios::in | ios::out);
break;
}
} //end switch(writeMode)

//file reader set to the file buffer.
reader = new istream(file->rdbuf());

//don't create a writer if we are only looking to open the file
if(writeMode != FS_OPEN_ONLY)
//file writer
writer = new ostream(file->rdbuf());
else
//just to be safe, set the unused writer to NULL
writer = NULL;
}

//Deconstructor
//Closes any open files, and deletes the file reader and writer
cFileSys::~cFileSys()
{
if(file)
{
file->close();
delete file;
file = NULL;
}

if(writer)
{
delete writer;
writer = NULL;
}

if(reader)
{
delete reader;
reader = NULL;
}
}
//resets the file pointer, so we can read from the start
void cFileSys::resetFilePtr()
{
//seek to 0 bytes from the begining
reader->seekg( 0, ios_base::beg );
}

//read one line from the file into the passed string
//either maxChars - 1 in length, or a "/n" char is hit
int cFileSys::readLine(char *output, UINT maxChars, char endChar)
{
if(!reader)
{
lastError = FS_READER_INIT_ERROR;
return(FS_READER_INIT_ERROR);
}

if(!reader->getline(output,maxChars,endChar))
return(FS_READ_ERROR);

return(FS_SUCCESS);
}

//hack because opening with ios::in | ios::out doesn't seem to create the file
//if it doesn't exist.
void cFileSys::createFile(char *fName)
{

fstream *tempFile = new fstream(fName, ios::out);

if(tempFile)
{
tempFile->close();
delete tempFile;
tempFile = NULL;
}

}


/*int cFileSys::deleteFile()
{
if(file)
{
file->close();

file = NULL;
}

if(!DeleteFile((LPCWSTR)fName))
{
lastError = FS_DELETE_FAIL;
return(FS_DELETE_FAIL);
}

return (FS_DELETE_SUCCESS);
}
*/


bool cFileSys::closeFile()
{
if(file)
{
file->close();
delete file;
file = NULL;
}

if(writer)
{
delete writer;
writer = NULL;
}

if(reader)
{
delete reader;
reader = NULL;
}

return (true);
}



int cFileSys::openFile(char* fileName, int action, int mode)
{
//set internal filename
fName = fileName;

//set the internal mode
writeMode = mode;


writeAction = action;

//if we want to create a new file, call the createfile function
if(action == FS_CREATE)
createFile(fileName);

//depending on what mode we want to open the file in,
//changes the parameters that are passed to the fstream constructor
switch(writeMode)
{
case FS_BINARY:
{
file = new fstream(fName, ios::in | ios::out | ios::binary);
break;
}

case FS_TEXT:
{
file = new fstream(fName, ios::in | ios::out);
break;
}
} //end switch(writeMode)

//file reader set to the file buffer.
reader = new istream(file->rdbuf());

//don't create a writer if we are only looking to open the file
if(writeMode != FS_OPEN_ONLY)
//file writer
writer = new ostream(file->rdbuf());
else
//just to be safe, set the unused writer to NULL
writer = NULL;

return FS_SUCCESS;

}

string cFileSys::getLastError()
{
switch(lastError)
{
case FS_WRITER_INIT_ERROR:
{
return("The writer failed to open the specified file.");
break;
}
case FS_WRITE_ERROR:
{
return("There was an error writing to the file.");
break;
}
case FS_READER_INIT_ERROR:
{
return("The reader failed to open the specified file.");
break;
}
case FS_READ_ERROR:
{
return("There was an error reading from the file.");
break;
}
case FS_UNKOWN_ERROR:
{
return("An unkown error happend. Perhpas you passed an unsupported parameter?");
break;
}
case FS_DELETE_FAIL:
{
return("There was an error deleting the specified file.");
break;
}
case FS_MEMORY_ALLOC_ERROR:
{
return("There was an error allocating the string buffer. Perhaps you are trying to read a file written in text mode in binary mode?");
break;
}
case FS_FUNCTION_ERROR:
{
return("This function cannot be used in text mode.");
break;
}
default:
{
return("There have been no errors.");
break;
}

}
}



//write a string to the file

template<> int cFileSys::write<std::string>(std::string input)
{
//depending on the write mode, the actual write code is different
switch(writeMode)
{
case FS_BINARY:
{
//check if the writer is invalid
//this will be set if the file doesn't exist, and we are trying to append to it.
if(!writer)
{
lastError = FS_WRITER_INIT_ERROR;
return (FS_WRITER_INIT_ERROR);
}

//if we want to append, seek to the end of the file
if(writeAction == FS_APPEND)
writer->seekp(0 , ios::end);

// Size of the string
UINT stringSize = UINT(input.size());

// Write the length of the string to the file
if(!writer->write((char*)&stringSize, sizeof(UINT)))
{
lastError = FS_WRITE_ERROR;
return (FS_WRITE_ERROR);
}

// Write the string to the file
if(!writer->write(input.c_str(), stringSize))
{
lastError = FS_WRITE_ERROR;
return(FS_WRITE_ERROR);
}

//if we get this far, everything worked, so return success
return FS_SUCCESS;


}
case FS_TEXT:
{
if(!writer)
{
lastError = FS_WRITER_INIT_ERROR;
return (FS_WRITER_INIT_ERROR);
}


// Size of the string
UINT stringSize = UINT(input.size());

//if we want to append, seek to the end of the file
if(writeAction == FS_APPEND)
writer->seekp(0 , ios::end);

// Write the string to the file
if(!writer->write(input.c_str(), stringSize))
{
lastError = FS_WRITE_ERROR;
return(FS_WRITE_ERROR);
}

return(FS_SUCCESS);

}
//An incorret parameter passed?
default:
{
lastError = FS_UNKOWN_ERROR;
return(FS_UNKOWN_ERROR);
}
}
}


//read a string from file, starting at the start
//unless in FS_TEXT mode, pass NULL for parms 2 and 3

template<> int cFileSys::read<std::string>(std::string &output)//, UINT maxChars, char endChar)
{
switch(writeMode)
{
case FS_BINARY:
{
//check is the reader is invalid
if(!reader)
{
lastError = FS_READER_INIT_ERROR;
return (FS_READER_INIT_ERROR);
}

//size of the string
UINT stringSize = 0;



// Read the length of the string from the file
if(!reader->read((char*)&stringSize, sizeof(UINT)))
{
lastError = FS_READ_ERROR;
return (FS_READ_ERROR);
}

//create a buffer to dump the string to
char *buffer;
try
{
buffer = new char[stringSize + 1];
}
catch(...)
{
lastError = FS_MEMORY_ALLOC_ERROR;
return (FS_MEMORY_ALLOC_ERROR);
}

// Read the string from the file
reader->read(buffer, stringSize);

// NULL-terminate the string
buffer[stringSize] = '\0';

//set the output passed to the function to be the buffer
output = buffer;

//deletes the string buffer
delete[] buffer;

//if we got this far, return success
return (FS_SUCCESS);
}
case FS_TEXT:
{

char endChar = '\n';

//max 100 chars
UINT maxChars = 100;

char *charTemp = new char[maxChars];

if(!readLine(charTemp,maxChars,endChar))
{
lastError = FS_READ_ERROR;
return(FS_READ_ERROR);
}

output = charTemp;

delete[] charTemp;

return(FS_SUCCESS);
}
default:
{
lastError = FS_UNKOWN_ERROR;
return(FS_UNKOWN_ERROR);
}
}
}



Sneftel: I don't think the article addresses all of the problems in the code (I haven't read it in detail). It doesn't talk about template (member) function specializations.


jfl.

Share this post


Link to post
Share on other sites
Quote:
From TFA:
There are two notable exceptions to the "no function bodies in header files", because although they look like function bodies, they aren't exactly the same.

The first exception is that of template functions. Most compilers and linkers can't handle templates being defined in different files to that which they are used in, so templates almost always need to be defined in a header so that the definition can be included in every file that needs to use it. Because of the way templates are instantiated in the code, this doesn't lead to the same errors that you would get by defining a normal function in a header. This is because templates aren't compiled at the place of definition, but are compiled as they are used by code elsewhere.

The second exception is inline functions, briefly mentioned earlier. An inline function is compiled directly into the code, rather than called in the normal way. This means that any translation unit where the code 'calls' an inline function needs to be able to see the inner workings (ie. the implementation) of that function in order to insert that function's code directly. This means that a simple function prototype is insufficient for calling the inline function, meaning that wherever you would normally just use a function prototype, you need the whole function body for an inline function. As with templates, this doesn't cause linker errors as the inline function is not actually compiled at the place of definition, but is inserted at the place of calling.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Again, go ahead and behold the article. Specifically, the last three paragraphs before "Other Considerations".

Ah. My apologies. So I need to move all the none templated functions to a .cpp file then?

Share this post


Link to post
Share on other sites
jflanglois
Thanks, that answered a lot of questions. Only thing that really blew me away was why is

// To avoid specialization/general template "collision"
template<> int cFileSys::write< std::string >( std::string input );

there? and why is it defined in the .cpp instead of the .h like the other templated fns?

Cheers

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!