Page 1 of 1

namespace, seperating decleration and implementation

Posted: Sun Aug 29, 2010 10:08 pm
by short
Hey guys, probably something ridiculous but consider the following if you will:

Debug.h

Code: Select all

#pragma once
#define BUFFER_SIZE 1028

#include <string>
#include <fstream>
#include <assert.h>
#include <stdarg.h> // va_arg

namespace Debugger
{
	// member variables
	const unsigned int m_sizebuffer = BUFFER_SIZE;
	std::ofstream m_file;
	bool m_fileopened;
	char m_buffer[m_sizebuffer];
	va_list m_vl;

	void open_file();
	void close_file();
	void print(const char* fmt, ...);
}

debug.cpp

Code: Select all


void Debugger::open_file()
{
	// file cannot already be opened.
	assert(!m_fileopened);
	m_file.clear(); // clear contents of debug file.

	//if directory already exists nothing *SHOULD* happen. platform dependent.
	system("mkdir Data");

	m_file.open("./Data/ErrorLog.txt");	// hard-coding filename is intentional
	if(m_file.is_open() )
	{
		m_fileopened = true;
		print("Debug: successfully loaded file './Data/ErrorLog.txt' \n" );
	}
}

void Debugger::close_file()
{
	if(m_fileopened)
	{
		m_file.close();
		m_fileopened = false;
	}
}
void Debugger::print(const char* fmt, ...)
{
	if(!m_fileopened)
	{
		open_file();
		print("Debug file opened. \n");
	}
	int retval = 0;
			
	va_start(m_vl, fmt);
	retval = vsnprintf_s(m_buffer, m_sizebuffer, m_sizebuffer, fmt, m_vl);
	va_end(m_vl);
	m_file << m_buffer;
	m_file.flush();

	assert(retval > 0);
}
main.cpp

Code: Select all

#include "stdlib.h"
#include "Debug.h"

int main(int argc, char* argv[])
{
	Debugger::print("this should work~! yay");

	return EXIT_SUCCESS;
}

When I run the above code, I receive a linker error telling me it does not know what the symbol Debugger::print is.

However, if I remove Debug.cpp and place the implementation of Debug.cpp into Debug.h below the declaration it works just fine. I've been trying to find a way to simplify a debug class without the need for passing references around for a debug object.

Does anyone know why this happens? Thanks, I'll be glad to add any details I may have forgotten to post.

Re: namespace, seperating decleration and implementation

Posted: Mon Aug 30, 2010 1:02 am
by WSPSNIPER
one thing i noticed but dont know if it matters is you dont use extern on your functions ... i always use classes so idk but another thing you could do is use using namespace Debug; in the .cpp file

Re: namespace, seperating decleration and implementation

Posted: Mon Aug 30, 2010 6:52 am
by Trask
short wrote:Hey guys, probably something ridiculous but consider the following if you will:

Debug.h

Code: Select all

#pragma once
#define BUFFER_SIZE 1028

#include <string>
#include <fstream>
#include <assert.h>
#include <stdarg.h> // va_arg

namespace Debugger
{
	// member variables
	const unsigned int m_sizebuffer = BUFFER_SIZE;
	std::ofstream m_file;
	bool m_fileopened;
	char m_buffer[m_sizebuffer];
	va_list m_vl;

	void open_file();
	void close_file();
	void print(const char* fmt, ...);
}

debug.cpp

Code: Select all


void Debugger::open_file()
{
	// file cannot already be opened.
	assert(!m_fileopened);
	m_file.clear(); // clear contents of debug file.

	//if directory already exists nothing *SHOULD* happen. platform dependent.
	system("mkdir Data");

	m_file.open("./Data/ErrorLog.txt");	// hard-coding filename is intentional
	if(m_file.is_open() )
	{
		m_fileopened = true;
		print("Debug: successfully loaded file './Data/ErrorLog.txt' \n" );
	}
}

void Debugger::close_file()
{
	if(m_fileopened)
	{
		m_file.close();
		m_fileopened = false;
	}
}
void Debugger::print(const char* fmt, ...)
{
	if(!m_fileopened)
	{
		open_file();
		print("Debug file opened. \n");
	}
	int retval = 0;
			
	va_start(m_vl, fmt);
	retval = vsnprintf_s(m_buffer, m_sizebuffer, m_sizebuffer, fmt, m_vl);
	va_end(m_vl);
	m_file << m_buffer;
	m_file.flush();

	assert(retval > 0);
}
main.cpp

Code: Select all

#include "stdlib.h"
#include "Debug.h"

int main(int argc, char* argv[])
{
	Debugger::print("this should work~! yay");

	return EXIT_SUCCESS;
}

When I run the above code, I receive a linker error telling me it does not know what the symbol Debugger::print is.

However, if I remove Debug.cpp and place the implementation of Debug.cpp into Debug.h below the declaration it works just fine. I've been trying to find a way to simplify a debug class without the need for passing references around for a debug object.

Does anyone know why this happens? Thanks, I'll be glad to add any details I may have forgotten to post.
Try this:

debug.h

Code: Select all

#pragma once
#define BUFFER_SIZE 1028

#include <string>
#include <fstream>
#include <assert.h>
#include <stdarg.h> // va_arg

namespace Debugger
{
	// member variables
	const unsigned int m_sizebuffer = BUFFER_SIZE;
	std::ofstream m_file;
	bool m_fileopened;
	char m_buffer[m_sizebuffer];
	va_list m_vl;

	void open_file();
	void close_file();
	void print(const char* fmt, ...);
};  

extern Debugger::Debugger *g_debug;

debug.cpp

Code: Select all



namespace Debugger
{

void Debugger::open_file()
{
	// file cannot already be opened.
	assert(!m_fileopened);
	m_file.clear(); // clear contents of debug file.

	//if directory already exists nothing *SHOULD* happen. platform dependent.
	system("mkdir Data");

	m_file.open("./Data/ErrorLog.txt");	// hard-coding filename is intentional
	if(m_file.is_open() )
	{
		m_fileopened = true;
		print("Debug: successfully loaded file './Data/ErrorLog.txt' \n" );
	}
}

void Debugger::close_file()
{
	if(m_fileopened)
	{
		m_file.close();
		m_fileopened = false;
	}
}
void Debugger::print(const char* fmt, ...)
{
	if(!m_fileopened)
	{
		open_file();
		print("Debug file opened. \n");
	}
	int retval = 0;
			
	va_start(m_vl, fmt);
	retval = vsnprintf_s(m_buffer, m_sizebuffer, m_sizebuffer, fmt, m_vl);
	va_end(m_vl);
	m_file << m_buffer;
	m_file.flush();

	assert(retval > 0);
}
}
main.cpp

Code: Select all

#include "stdlib.h"
#include "Debug.h"
using namespace Debugger;

int main(int argc, char* argv[])
{
	g_debug->print("this should work~! yay");

	return EXIT_SUCCESS;
}
Your programs have to point to the debug.h file's location... so if debug.h isn't in the same directory, make sure you're specifying where it is. In your debug.cpp file, you have to make sure you're doing the code under the namespace, or technically it doesn't exist(unless you make the .h/.cpp one big program, which is silly to do). Also, in your program that's access the namespace, you have to specify to use it.

Also, I agree with WSPSNIPER on the extern function thing as I personally like to use it. I added it at the bottom of your debug.h file. Any function you'd call from your namespace of Debugger would have to be proceeded by g_debug.

Re: namespace, seperating decleration and implementation

Posted: Mon Aug 30, 2010 6:14 pm
by short
first off thank you for replying, secondly I tried what you suggested and my (vs 2008) compiler had absolutely no idea what

Code: Select all

extern Debugger::Debugger *g_debug;

was. I tried to infer what you meant but I could not figure out what you were trying to do.

I did some more research, and i came up with the following changes (now that the 'extern' keyword makes sense to me).


Debug.h

Code: Select all

#define BUFFER_SIZE 1028

#include <string>
#include <fstream>
#include <assert.h>
#include <stdarg.h> // va_arg

#ifndef _DEBUG_H_
#define _DEBUG_H_
namespace Debugger
{
	// member variables
	extern const unsigned int m_sizebuffer;
	extern std::ofstream m_file;
	extern bool m_fileopened;
	extern char m_buffer[BUFFER_SIZE];
	extern va_list m_vl;

	/* 
		'extern' keyword there to remind myself that 
		all functions are implicitly extern, so
		declaring variables with 'extern' is more intuitive. 
		Will remove when the 'extern' keyword becomes second nature to me.
	*/
	extern void open_file(); 
	extern void close_file();
	extern void print(const char* fmt, ...);
}

#endif
Debug.cpp

Code: Select all

#ifndef _DEBUG_H_
#include "Debug.h"
#endif

namespace Debugger
{
	const unsigned int m_sizebuffer = BUFFER_SIZE;
	std::ofstream m_file;
	bool m_fileopened;
	char m_buffer[BUFFER_SIZE];
	va_list m_vl;

	void Debugger::open_file()
	{
		// file cannot already be opened.
		assert(!m_fileopened);
		m_file.clear(); // clear contents of debug file.

		//if directory already exists nothing *SHOULD* happen. platform dependent.
		system("mkdir Data");

		m_file.open("./Data/ErrorLog.txt");	// hard-coding filename is intentional
		if(m_file.is_open() )
		{
			m_fileopened = true;
			print("Debug: successfully loaded file './Data/ErrorLog.txt' \n" );
		}
	}

	void Debugger::close_file()
	{
		if(m_fileopened)
		{
			m_file.close();
			m_fileopened = false;
		}
	}

	/*
		WARNING: Should only accept c-style strings only. If output is ever cryptic double check 
		that we are not passing c++ Strings instead of char*, do not know how to differentiate if
		fmt is a c-style string (char*) or a C++ String.
	*/
	void Debugger::print(const char* fmt, ...)
	{
		if(!m_fileopened)
		{
			open_file();
			print("Debug file opened. \n");
		}
		int retval = 0;
				
		va_start(m_vl, fmt);
		retval = vsnprintf_s(m_buffer, m_sizebuffer, m_sizebuffer, fmt, m_vl);
		va_end(m_vl);
		m_file << m_buffer;
		m_file.flush();

		assert(retval > 0);
	}
}
main.cpp

Code: Select all

#include "stdlib.h"


#ifndef _DEBUG_H_
#include "Debug.h"
#endif

int main(int argc, char* argv[])
{
	//Debugger::print("this should work~! yay");
	
	return EXIT_SUCCESS;
}
If I un-comment the print line in the main function I get the following error:

Code: Select all

1>LINK : C:\Users\Benjamin\Desktop\vs projects (c++)\Game Debugger\debug class\Debug\Smashteroids.exe not found or not built by the last incremental link; performing full link
1>main.obj : error LNK2019: unresolved external symbol "void __cdecl Debugger::print(char const *,...)" (?print@Debugger@@YAXPBDZZ) referenced in function _main
1>C:\Users\Benjamin\Desktop\vs projects (c++)\Game Debugger\debug class\Debug\Smashteroids.exe : fatal error LNK1120: 1 unresolved externals
If I add #include "Debug.cpp" to main.cpp this code works just fine. The compiler is somehow missing the implementation of the open, close, and print functions unless I either put the implementation in the .h file or #include Debug.cpp.

Do you have any idea why it is not seeing the implementation in Debug.cpp?



edit: I don't think it matters, but currently debug.h and debug.cpp are in one project and main.cpp is in another project (both within the same solution however). main.cpp is being built as an executable, and debug.h and debug.cpp are being compiled as a dynamic library (dll).

Re: namespace, seperating decleration and implementation

Posted: Mon Aug 30, 2010 10:34 pm
by qpHalcy0n
Yes, it most certainly DOES matter that you're trying to export to a DLL. That changes some things.

The first thing I'm noticing is that it's not evident that you're actually exporting or importing the namespace functions. Namespace components are visible, functions must be exported when building a DLL project. It's not done automatically.
If it indeed has built a dll and/or lib from the aforementioned code, then be sure that you're referencing the DLL or linking to the LIB, otherwise those functions will not be resolved by the invoking executable. So be sure that the DLL and LIB are actually built then take it from there.

Re: namespace, seperating decleration and implementation

Posted: Tue Aug 31, 2010 1:07 am
by short
Qp, I changed the output of the debug project from dynamic library to static library and it worked magically. Do you have any ideas as why a .lib would work just fine, but not a dll?

Re: namespace, seperating decleration and implementation

Posted: Tue Aug 31, 2010 9:38 am
by qpHalcy0n
Yes, well what's happening is this.

If you export the symbols in a DLL project (as opposed to a statically linked DLL, which requires a LIB...you can still generate a lib from a DLL project as well), it requires some extra steps to load the function symbols into the invoker's memory. (The executable using the DLL). You would use LoadLibrary to load the DLL into client memory. The function GetProcAddress then essentially obtains a function pointer for you from said DLL. This is a pre-process and only must be done once. Here you're using Win32 at runtime to load in the function addresses.

If you compile and export as a statically linked lib, the LIB acts as a liason between the invoker and the DLL. It is a library of exported function input addresses which are used to lookup the functions in the DLL. This is done when you link to the lib from the calling project. Thusly, you do not necessarily have to explicitly load the library and get all the function hooks. This negates the need to use the Win32 library loading functions explicitly, although you still can.