I can't get files linked properly

Started by
8 comments, last by Shakedown 16 years, 10 months ago
I can never seem to get these right, but I can't see any difference from other projects. When I try and compile I get linker errors, here are my pages: ERROR

Deleting intermediate and output files for project 'Scripting', configuration 'Debug|Win32'
Compiling...
Implementations.cpp
main.cpp
Generating Code...
Linking...
main.obj : error LNK2019: unresolved external symbol "public: void __thiscall VirtualMachine::Execute(unsigned int)" (?Execute@VirtualMachine@@QAEXI@Z) referenced in function _main
C:\Documents and Settings\Nate\My Documents\Visual Studio 2005\Projects\Scripting\Debug\Scripting.exe : fatal error LNK1120: 1 unresolved externals
Build log was saved at "file://c:\Documents and Settings\Nate\My Documents\Visual Studio 2005\Projects\Scripting\Scripting\Debug\BuildLog.htm"
Scripting - 2 error(s), 0 warning(s)
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

Declarations.h

#ifndef DECLARATIONS_H
#define DECLARATIONS_H

#include <vector>

enum opcode
{
	op_talk, 
	op_end 
};

// the basic instruction, currently just encapsulating code
class Instruction
{
private:
	opcode		_code;
	//char*		_data; // additional data, currently not used

public:
	Instruction(opcode code) : _code(code) {}
	opcode Code() const { return _code; }
};

// the basic script, currently just encapsulating an arrayed list of instructions
class Script
{
private:
	std::vector<Instruction> _instrList;

public:
	Script(const std::vector<Instruction>& instrList) : _instrList(instrList) {}
	const Instruction* InstrPtr() const { return &_instrList[0]; }
};

// rudimentary virtual machine wiht methods inlined for convenience
class VirtualMachine
{
private:	// useful abstractions
	// pointers used as non-modifying dynamic references
	typedef const Script*		ScriptRef;
	typedef const Instruction*	InstrRef;

private:	// data members
	std::vector<Script>	_scriptList;
	ScriptRef			_scriptPtr;		// current script
	InstrRef			_instrPtr;		// root instruction
	InstrRef			_instr;			// current instruction
	size_t				_scriptCount;	// track the loaded scripts

private:	// utilities
	size_t AddScript(const Script& script);	// add script to list and retrieve id
	void SelectScript(size_t index);		// set current script by id

public:
	VirtualMachine() : _scriptPtr(0), _instrPtr(0), _instr(0), _scriptCount(0) {}
	// a very basic interface
	inline void Execute(size_t scriptId);
	size_t Load(const Script& script)	{ return AddScript(script); }

};

#endif


Implementations.cpp

#include "Declarations.h"
#include <iostream>
#include <cassert>

size_t VirtualMachine::AddScript(const Script& script)
{
	_scriptList.push_back(script);
	return _scriptCount++;
}

void VirtualMachine::SelectScript(size_t index)
{
	assert(index < _scriptCount);	// make sure the id is valid
	_scriptPtr = &_scriptList[index];
	_instrPtr = _scriptPtr->InstrPtr();
}

void VirtualMachine::Execute(size_t scriptId)
{
	SelectScript(scriptId);		// select our _instrPtr by scriptId
	_instr = _instrPtr;			// set out iterator to beginning
	while(_instr)
	{
		switch(_instr->Code())
		{
		case op_talk:
			std::cout << "I am talking." << std::endl;
			++_instr;	// iterate
			break;
		case op_end:
			_instr = 0;	// discontinue the loop
			break;
		}
	}
}

main.cpp

#include "Declarations.h"

int main()
{
	VirtualMachine vm;

	// build the script
	std::vector<Instruction> InstrList;
	InstrList.push_back(Instruction(op_talk));	// talk twice
	InstrList.push_back(Instruction(op_talk));
	InstrList.push_back(Instruction(op_end));	// then end
	Script script(InstrList);

	// load the script and save the id
	size_t scriptID = vm.Load(script);

	// execute the script by its id
	vm.Execute(scriptID);
}

Advertisement
When the compiler compiles main.cpp, Execute is declared as inline but no function definition is provided. Either move the function definition to the header or don't make it inline.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
You use "inline", but did't provide the implementation in the header. Remove the "inline", it may not be particularly fast (typically it should only be used for tiny functions).
Also, why won't this work? Nothing is being printed to the screen now that I've added the char data. Here's the source again, all in one file this time:

main.cpp
#include <vector>#include <cassert>#include <iostream>enum opcode{	op_talk,	op_print,       <---------- ADDED	op_end };// the basic instruction, currently just encapsulating codeclass Instruction{private:	opcode		_code;	char*		_data; // additional data, currently not used   <--------- ADDED	public:	Instruction(opcode code) : _code(code), _data(0) {}	Instruction(opcode code, const char* data, size_t dataSize) : _code(code), _data(new char[dataSize]) { memcpy(_data, data, dataSize); }     <--------- ADDED	~Instruction() { delete[] _data; }      <---------- ADDED	opcode Code() const { return _code; }	const char* Data() const { return _data; }	// read the data  <----------- ADDED};// the basic script, currently just encapsulating an arrayed list of instructionsclass Script{private:	std::vector<Instruction> _instrList;public:	Script(const std::vector<Instruction>& instrList) : _instrList(instrList) {}	const Instruction* InstrPtr() const { return &_instrList[0]; }};// rudimentary virtual machine wiht methods inlined for convenienceclass VirtualMachine{private:	// useful abstractions	// pointers used as non-modifying dynamic references	typedef const Script*		ScriptRef;	typedef const Instruction*	InstrRef;private:	// data members	std::vector<Script>	_scriptList;	ScriptRef			_scriptPtr;		// current script	InstrRef			_instrPtr;		// root instruction	InstrRef			_instr;			// current instruction	size_t				_scriptCount;	// track the loaded scriptsprivate:	// utilities	size_t AddScript(const Script& script);	// add script to list and retrieve id	void SelectScript(size_t index);		// set current script by idpublic:	VirtualMachine() : _scriptPtr(0), _instrPtr(0), _instr(0), _scriptCount(0) {}	// a very basic interface	inline void Execute(size_t scriptId);	size_t Load(const Script& script)	{ return AddScript(script); }};size_t VirtualMachine::AddScript(const Script& script){	_scriptList.push_back(script);	return _scriptCount++;}void VirtualMachine::SelectScript(size_t index){	assert(index < _scriptCount);	// make sure the id is valid	_scriptPtr = &_scriptList[index];	_instrPtr = _scriptPtr->InstrPtr();}void VirtualMachine::Execute(size_t scriptId){	SelectScript(scriptId);		// select our _instrPtr by scriptId	_instr = _instrPtr;			// set out iterator to beginning	while(_instr)	{		switch(_instr->Code())		{		case op_talk:			std::cout << "I am talking." << std::endl;			++_instr;	// iterate			break;		case op_print:          <------------- ADDED			std::cout << _instr->Data() << std::endl;	// print data			++_instr;			break;		case op_end:			_instr = 0;	// discontinue the loop			break;		}	}}int main(){	VirtualMachine vm;	// simulate some external data	char* buffer = "this is printed data.";	// build the script	std::vector<Instruction> InstrList;	InstrList.push_back(Instruction(op_talk));	// talk twice	InstrList.push_back(Instruction(op_print, buffer, strlen(buffer)+1));	InstrList.push_back(Instruction(op_end));	// then end	Script script(InstrList);	// load the script and save the id	size_t scriptID = vm.Load(script);	// execute the script by its id	vm.Execute(scriptID);}


Ah yes, the inline problem makes sense, they link properly now, thank you!
It's kind of odd though; compilers usually treat your inline declarations as "not inline" if it doesn't make sense to them. I guess this one slipped by, and your linker choked on it :-).
0) Don't use raw char* for this. Use std::string or std::vector<char>, depending on whether or not your data is really string data or a buffer of bytes.

1) You don't have a copy constructor or assignment operator. Instruction is copied using the default (member-wise) copy constructor, which just aliases the underlying pointer. When any of the Instruction instances aliasing the pointer is destroyed, the memory is released. Your data vanishes. Remember the Rule of Three.

2) Stop using raw char* for your data.

3) Use a debugger and step through the execution, you'll be able to examine exactly what goes wrong and when. It will help you isolate the problem better.

4) Stop using raw char* for your data.
Thanks jptetrie. I'm following an article, and the author uses the char*, saying that later in the series it will feel more natural to be using char* rather than strings. But, I can't figure out how to step through the debugger with VC++, or much less use the debugger at all. I need to find a tutorial on it, perhaps I'll go back to the microsoft page.
Quote:
and the author uses the char*, saying that later in the series it will feel more natural to be using char* rather than strings.

He's wrong. You can use std::string or std::vector<char> (as appropriate) just as naturally, if not more so, since you don't have to be screwing around with all this manual memory management. What article are you reading?

Quote:
I need to find a tutorial on it, perhaps I'll go back to the microsoft page.

It's rather easy; the MSDN pages have some good articles on using the debugger, and Superpig wrote an article here about debugging.
Quote:Original post by jpetrie
He's wrong. You can use std::string or std::vector<char> (as appropriate) just as naturally, if not more so, since you don't have to be screwing around with all this manual memory management. What article are you reading?

It's rather easy; the MSDN pages have some good articles on using the debugger, and Superpig wrote an article here about debugging.


The article is here, about scripting. Thank you for the link!

This topic is closed to new replies.

Advertisement