Class Pointers

My goal is to create a sort of template file(s) for handling user-input into the command line that I could use for any console application that I'll be creating in the future. And it looks like this.

main.cpp
1
2
3
4
#include "stdafx.h"
int main(){
     return;
}


command.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "string.h"
#include <iostream>

class cCommand{
public:
     std::string m_name;
     int m_numchild;
     bool m_haschild;
     void (*_HandleCmd)();
};

class cCmdScript{
     static int m_numscript; //keeps a count of the number of command scripts present.
     std::string m_scriptname;
public:
     cCmdScript(); //default. never used
     cCmdScript(std::string name); 
     static int GetNumScript();
     virtual cCommand* GetCommandTable();
};
cCmdScript::m_numscript=0;

class cCmdLoader{
public:
     static cCmdScript m_scripttable[10]; //table that holds all present command scripts
};


command.cpp
1
2
3
4
5
6
7
8
9
10
11
#include "command.h"
#include "stdAfx.h"
#include "string.h"

cCmdScript::cCmdScript(){} //default. never used.
cCmdScript::cCmdScript(std::string name):m_name(name){
     cCmdScript::m_numscript++;
     cCmdLoader::m_scripttable[cCmdScript::GetNumScript()-1]=*this;
}
cCmdScript::GetNumScript(){return cCmdScript::m_numscript;} 
cCmdScript::GetCommandTable(){return NULL;} //virtual. This override is never used. always called by a derived instance. 


The code works (or is intended to work) like this; the cCmdScript class is the base class for all commands that I'll use. For example, if I wanted the user to have the option to print some statement or variable to the console, then I would create a file called print.cpp, include command.h, and then code
class print_command:public cCmdScript("print_command")
The cCmdScript constructor (in command.cpp, cCmdScript::cCmdScript(std::string name) then increments the m_numscript variable in the cCmdScript class, and then it copies the object that was just created (class print_command) to the static table of cCmdScript objects inside the cCmdLoader class. The copying occurs in line 8 of command.cpp.

There are a lot of problems here.
1. In the cCmdLoader class I create an array of cCmdScript objects. I don't know how many commands I'm going to add though, so I allocated 10 because that would probably be enough (there's definitely a better way to do this. Most likely with the new keyword. But I don't know how to make that work in this case). The only problem though is that I'm fairly certain that by creating a pointer to an array of cCmdScript objects, the default constructor is called 10 times right off the bat. Which isn't good. I don't want cCmdLoader::m_scripttable to be 10 empty cCmdScript objects. I want cCmdLoader::m_script to be an array of all of the commands that have been derived from the cCmdScript class.

2. Line 8 in command.cpp isn't working properly as it is. A linker error occurs "1 unresolved symbol" Which doesn't make sense to me. All functions declared in command.h have been defined in command.cpp, and those that haven't been defined by the time Line 8 comes around doesn't matter because command.h has already forward declared it.

Thanks for reading.
You do realize that there do exist frameworks like this already and therefore your efforts are mainly self-educational?


You don't have to define the default constructor if it will not be called. You have a non-default constructor so compiler should not create implicitly default ctor either. Or put the default ctor into private: section.


Array and count?
1
2
cCmdScript::m_numscript=0;
static cCmdScript m_scripttable[10];

Or dynamic vector:
std::vector<cCmdScript> m_scripttable;

Or perhaps a map:
1
2
3
4
5
std::map<std::string, Functor> commands;

// Calling Elvis, if known
auto cmd = commands.find( "print_command" );
if ( commands.end() != cmd ) cmd->second();

Yes, I do realize that my efforts are mainly self-educational. My efforts in C++ have never been for any other reason haha. Thank you for your response. mapping is a good idea.
I would use a static dynamic array to store the list of commands. This way, you don't create an array each time you initialize a new command that is the size of the number of your commands with only 1 command in it. (-,-')

In any case, there's also another way to go: use std::list (or if you need indices, vectors). You should make them static, again, so that there is only ever 1 instance of them.
My apologies, @IWishIKnew; in my attempt to be as fully cooperative and transparent as possible so as to help those help me, I guess I crossed the line into annoying.

I've never used vectors before, but how does this look?

command.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include “string.h”
#include “vector.h”

class cCommand{
	std::string m_name;
	bool m_haschild;
	int m_numchild;
	void (*_HandleCmd)();
	cCommand* m_childtable;
};

class cCmdScript{
	std::string m_name;
public:
	cCmdScript();
	cCmdScript(std::string name);
	virtual cCommand* GetCommandTable();
};

class cCmdLoader{
public:
	typedef cCmdScript* scriptptr;
	static std::vector<scriptptr> m_scripttable;
};


command.cpp
1
2
3
4
5
6
7
8
9
10
#include “command.h”
#include “string.h”
#include <iostream>
#include “vector.h”

cCmdScript::cCmdScript(){}
cCmdScript::cCmdScript(std::string name):m_name(name){
	cCmdLoader::m_scripttable.emplace_back(this);
}
cCommand* cCmdScript::GetCommandTable(){return NULL;}
Last edited on
Just got to the laptop with my IDE and tried out the code I posted above. Unfortunately I'm getting the same Unresolved External Symbol coming from line 8 in command.cpp
cCmdLoader::m_scripttable.emplace_back(this);
Everything compiles fine when I omit that line. For the life of me I can't figure out the problem with that line of code.
Last edited on
bump.
std::vector::emplace_back() was added in C++11:

http://www.cplusplus.com/reference/vector/vector/emplace_back/

Are you sure you're enabling C++11 support when compiling?
Shit I have no idea... Where in Visual Studio is that option?
bool haschild and int numchild need to be static.

I would also consider not using cmdloader class. Just add a static variable to the base class. You can use push_back member function of std::vector<> to add commands. This also has the added bonus of making the vector available to all of the child classes of the base class.
Last edited on
Thank you, IWishIKnew, I will definitely take your advice once I find a way to get rid of these unresolved symbol errors... I'm using Visual Studio Express 11.0. And the vector header is present and intellisense also verified the existence of the push_back() function (I was previously using the emplace function). Unfortunately I'm still getting an unresolved symbol error.

I did some searching around on the internet for how to enable C++11 in visual studio, but the only answers I got were along the lines of "its already 'enabled.'" I'm installing Visual Studio Express 2013 for Windows Desktop right now. I was using 2012 (the directory being Microsoft Visual Studio 11.0). Perhaps installing 2013 will fix it.
cleanly installed VSE2013 -> new Win32 Console Application -> moved the code posted above to the new project

Same error. Unresolved external symbol caused by
cCmdLoader::m_scripttable.push_back(this)
bump
#include <string> not #include "string.h"

Likewise with vector.

Also, in command.cpp you must define the cCmdLoader::m_scripttable object which is only declared in command.h.

If a constructor stores a pointer to the object, shouldn't a destructor remove the stored pointer?
Last edited on
Could you please explain the differences between <> include format and the ".h" format?

The next explanation makes sense; that m_scripttable is not defined, only declared, therefor the compiler complains about an external symbol. I can't believe I didn't realize that was the issue.
Now what is the best way to go about solving this? I want the static vector m_scripttable to be initialized as an empty vector with a size of 0, so that when command scripts start getting added, the only action that needs to be taken is using pop_back on the empty vector.

But isn't the m_scripttable object already defined at the same time it is declared. Doesn't the line
static std::vector<scriptptr> m_scripttable;
call the vector default constructor, consequently defining it?

And in the likely event that it doesn't call the default constructor, how would I go about calling the vector default constructor on the m_scripttable object that's already been declared?
Last edited on
Could you please explain the differences between <> include format and the ".h" format?


You're conflating two different things:

1) The difference between using <> and "" in an include statement. The convention is that you use <> for including standard/third-party header files, and "" for your own header files.

Some compilers (and other development tools) will treat these differently, e.g. giving warnings where appropriate in your own header files, but suppressing them in standard headers.

2) Whether or not a filename has a .h (or sometimes .hpp) extension. Basically, standard library headers don't, and all others (including ones you've written) do.

But isn't the m_scripttable object already defined at the same time it is declared. Doesn't the line
static std::vector<scriptptr> m_scripttable;
call the vector default constructor, consequently defining it?

No. Static data members are not instantiated when an object of the class is instantiated (because they exist even when no objects of the class have been instantiated). The line:

static std::vector<scriptptr> m_scripttable;

is therefore considered to be a declaration only; it is still necessary to provide a definition of the entity somewhere in the code. Because the variable is defined once and once only (because that's the rule for any entity with external linkage), this is usually done in a .cpp file, and for class data members, it should be done in the .cpp file containing the class implementation.

The definition should look like this:

std::vector<cCmdLoader::scriptptr> cCmdLoader::m_scripttable;

Note that because this definition is outside the class {}; block, you'll need to explicitly qualify both the name of the variable, and the scriptptr type, with the name of the class they belong to.
Last edited on
Topic archived. No new replies allowed.