namespace, seperating decleration and implementation

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
short
ES Beta Backer
ES Beta Backer
Posts: 548
Joined: Thu Apr 30, 2009 2:22 am
Current Project: c++, c
Favorite Gaming Platforms: SNES, PS2, SNES, SNES, PC NES
Programming Language of Choice: c, c++
Location: Oregon, US

namespace, seperating decleration and implementation

Post 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.
My github repository contains the project I am currently working on,
link: https://github.com/bjadamson
User avatar
WSPSNIPER
Chaos Rift Regular
Chaos Rift Regular
Posts: 145
Joined: Sun Jan 03, 2010 6:19 pm
Current Project: top down shooter
Favorite Gaming Platforms: ps3
Programming Language of Choice: c++

Re: namespace, seperating decleration and implementation

Post 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
User avatar
Trask
ES Beta Backer
ES Beta Backer
Posts: 738
Joined: Wed Oct 29, 2008 8:17 pm
Current Project: Building a 2D Engine
Favorite Gaming Platforms: Sega Genesis and Xbox 360
Programming Language of Choice: C/C++
Location: Pittsburgh, PA
Contact:

Re: namespace, seperating decleration and implementation

Post 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.
MarauderIIC wrote:You know those people that are like "CHECK IT OUT I just made Linux run on this piece of celery [or other random object]!!"? Yeah, that's Falco, but with ES.
Dear god, they actually ported ES to a piece of celery!
Martin Golding wrote: "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
User avatar
short
ES Beta Backer
ES Beta Backer
Posts: 548
Joined: Thu Apr 30, 2009 2:22 am
Current Project: c++, c
Favorite Gaming Platforms: SNES, PS2, SNES, SNES, PC NES
Programming Language of Choice: c, c++
Location: Oregon, US

Re: namespace, seperating decleration and implementation

Post 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).
My github repository contains the project I am currently working on,
link: https://github.com/bjadamson
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: namespace, seperating decleration and implementation

Post 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.
User avatar
short
ES Beta Backer
ES Beta Backer
Posts: 548
Joined: Thu Apr 30, 2009 2:22 am
Current Project: c++, c
Favorite Gaming Platforms: SNES, PS2, SNES, SNES, PC NES
Programming Language of Choice: c, c++
Location: Oregon, US

Re: namespace, seperating decleration and implementation

Post 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?
My github repository contains the project I am currently working on,
link: https://github.com/bjadamson
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: namespace, seperating decleration and implementation

Post 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.
Post Reply