• Advertisement
Sign in to follow this  

Too late, stupid compiler

This topic is 4795 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

Its 12am, and I started work at 6am this morning. Question about compiler error. I should really try and work this out myself, but since it is so late, I'm going to bed. But, as a compromise I'm posting in the beginners and not general (I don't think this is worthy of a general post :D ).

class CConsole
{
public:
/// A pointer to a function that returns void and takes a const vector<string>& parameter
typedef void (*console_function)(const vector<string>& );

	CConsole()
	{
		// Add some console functions to the console
		addItem("getCommandEcho", &CConsole::consoleFunctions, CTYPE_FUNCTION);      // Line 103
		addItem("setCommandEcho", &CConsole::consoleFunctions, CTYPE_FUNCTION);      // Line 104
	}

	void addItem(const string& strName, void* pointer, console_item_type_t type)
	{
		// create a console item
		console_item_t item(strName, pointer, type);

		// name == strName
		list<console_item_t>::iterator it = m_itemList.begin();
		list<console_item_t>::iterator it_end = m_itemList.end();
		for( ; it != it_end ; it++ ){
			// Check that an item with this name doesn't exist
			if( it->name == strName ){
				cout << "CConsole::addItem: Item with name " << strName << " already exists." << endl;
				print("Item with name " + strName + " already exists.");
				return;
			}
		}

		// add item to the list
		m_itemList.push_back(item);
	}

	void consoleFunctions(const vector<string>& vec)
	{
		if( vec[0] == "getCommandEcho" )
			// Print the output of getCommandEcho() to the console.
			print( vec[0] + " = " + BoolStr(getCommandEcho) );
		else if( vec[0] == "setCommandEcho" ){
			// If != "true" or != "false"
			if( vec.size() != 2 ||   vec[1] != "true" || vec[1] != "false" ){
				print( "Error in arguments." );
				return;
			}

			// Call setCommandEcho
			setCommandEcho( StrBool(vec[1]) );
		}

	}
};






Note: console_item_type_t is just an enum.
//Errors:
c:\downloads\naim downloads\programming\nehe\console\cconsole.h(103) : error C2664: 'addItem' : cannot convert parameter 2 from 
'void (__thiscall CConsole::*)(const class std::vector<class std::basic_string<char,struct std::char_traits<char>,class s
td::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)' to 
'void *'
        There is no context in which this conversion is possible
c:\downloads\naim downloads\programming\nehe\console\cconsole.h(104) : error C2664: 'addItem' : cannot convert parameter 2 from 
'void (__thiscall CConsole::*)(const class std::vector<class std::basic_string<char,struct std::char_traits<char>,class s
td::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)' to 
'void *'
        There is no context in which this conversion is possible
CConsole.cpp
c:\downloads\naim downloads\programming\nehe\console\cconsole.h(103) : error C2664: 'addItem' : cannot convert parameter 2 from 
'void (__thiscall CConsole::*)(const class std::vector<class std::basic_string<char,struct std::char_traits<char>,class s
td::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)' to 
'void *'
        There is no context in which this conversion is possible
c:\downloads\naim downloads\programming\nehe\console\cconsole.h(104) : error C2664: 'addItem' : cannot convert parameter 2 from 
'void (__thiscall CConsole::*)(const class std::vector<class std::basic_string<char,struct std::char_traits<char>,class s
td::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)' to 
'void *'
        There is no context in which this conversion is possible



When I try to cast the function addresses as (void*) I get what looks like most of the same text, except it is a type cast error. If you can reply, thanks very much. I appreciate the help. :D

Share this post


Link to post
Share on other sites
Advertisement
Hi,

&CConsole::consoleFunctions is a pointer to a member function which can only work with a class ...
eg.

CConsole console;
void (CConsole::*func)( std::vector< std::string> ) = &CConsole::consoleFunctions;

(console.*func)();

Share this post


Link to post
Share on other sites
Linky.

EDIT, from here:
Quote:


// Example 3(b): A nonstandard and
// nonportable hack
//
typedef void* (*FuncPtr)();
void* f() { return (void*)f; } // cast to void*
FuncPtr p = (FuncPtr)(f()); // cast from void*
p();

This isn't a solution because it doesn't satisfy the criteria of the question. Worse, it is dangerous because it deliberately defeats the type system, and it is onerous because it forces all users of f() to write casts. Worst of all, Example 3(b) is not supported by the standard. Although a void* is guaranteed to be big enough to hold the value of any object pointer, it is not guaranteed to be suitable to hold a function pointer; for example, on some platforms a function pointer is larger than an object pointer. Even if code like Example 3(b) happens to work on the compiler you're using today, it's not portable and may not work on another compiler, or even on the next release of your current compiler.

Share this post


Link to post
Share on other sites
Now that I think of it, (*((void**)(&funptr))) should allow you to bypass the compiler error.

Of course, a functor might be a better idea.

Share this post


Link to post
Share on other sites
I think you guys gave him the most horrible solutions. The answer is in fact quite simple:


// The function pointer is now a "function pointer to class member"
typedef void (CConsole::*ConsFn)(const vector<string>&);

CConsole()
{
// Add some console functions to the console
addItem("getCommandEcho", (CConsole::ConsFn)CConsole::consoleFunctions, CTYPE_FUNCTION); // Line 103
addItem("setCommandEcho", (CConsole::ConsFn)CConsole::consoleFunctions, CTYPE_FUNCTION); // Line 104
}



As you can see, I made the function pointer a class member function pointer(Otherwise the functions would have to be static), I removed the the address operator(&) and I cast the functions to (CConsole::ConsFn). The last cast is not always needed, as it works without cast in MSVC.NET 2003, but the 2002 compiler pukes without cast.

Calling the member function is a bit more work:

(this->*(MyFunctionPointer))(ArgList);



This is how I do it in my MUD, so I assume it should work in yours too.

Toolmaker

Share this post


Link to post
Share on other sites
Toolmaker, your solution does not work either because:

1. "addItem" expects a second parameter of type 'void*'
2. You pass to it a second parameter of type 'void (CConsole::*)(const vector<string>&)'
3. There is no legal conversion from 'void (CConsole::*)(const vector<string>&)' to 'void*'

Share this post


Link to post
Share on other sites
Ah, I overlooked that part. Now, why would you want a void* as 2nd param? I would just use the correct type myself.

Endar: You got shown ToohrVyk's solution and my solution, pick the one that best quited for this. (And that is, which one of the 2 you prefer).

Toolmaker

Share this post


Link to post
Share on other sites
Quote:
Original post by Toolmaker
Ah, I overlooked that part. Now, why would you want a void* as 2nd param? I would just use the correct type myself.


The second param is void* because functions aren't the only items that can be added. Its writtent to work so an item can be a variable and a function without having to write and call another function.

PS. I got the idea of doing this from the article on a quake-style console, and I've written most of the functions myself. So, if anyone has gone ahead and written this without waiting for the next article and can give me a pointer, please go ahead.

:D

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I assume the reason you want to have both variables and functions in the second argument is so that you can handle lines like "foo = bar".

class CConsole {
public:
typedef void (CConsole::*console_function)(const vector<string>& );
typedef cmdmap map<string, console_function>;
typedef varmap map<string, variable>; // however you represent the vars.
private:
cmdmap commands;
// The console_item_type_t is presumably no longer needed since it was
// presumably just distinguishing console_functions from variables.
// used to use a list.
varmap variables;
public:
typedef void (CConsole::*console_function)(const vector<string>& );

CConsole() {
// "Builtins"
addItem("getCommandEcho", &CConsole::consoleFunctions);
addItem("setCommandEcho", &CConsole::consoleFunctions);
}

void handleInput(const vector<string>& args) {
if (args.size() == 3 && args[1] == "=") {
// this is an assignment to a variable.
variables[args[0]] = variable(args[2]);
// Modify this as desired if you don't want vars to "autovivify".
} else { // a command invocation.
cmdmap::iterator it = commands.find(args[0]);
if (it != cmdmap::npos) {
// Found it. Access console_function from the iterator and invoke it.
*(it->second)(args);
} else {
// report error here. Note that assignments with a wrong number of
// arguments will fall through to here, currently.
// You might also want logic here so that if there is only one argument,
// it can be interpreted as a variable name (in which case the contents
// would be reported).
}
}
}

void addItem(const string& name, console_function* command) {
// Insert into the map, ensuring there is no duplicate.
if ((commands.find(name)) != cmdmap::npos) {
// already there.
cout << "CConsole::addItem: Item with name " << strName << " already exists." << endl;
print("Item with name " + strName + " already exists.");
// You should probably have a wrapper somewhere that handles that error
// reporting so you don't need both a cout and a console 'print' every
// time.
} else {
// Not found, so insert the new item.
commands[name] = command;
}
// See how much easier things are when you use the right datatype?
}

// Why not actually have two separate functions instead of re-checking the
// command name?
void consoleFunctions(const vector<string>& vec) {
if( vec[0] == "getCommandEcho" ) {
// you probably want to keep the "getCommandEcho" inside the variables
// map instead.
// Also, expecting the user to use case-sensitive commands is not so nice.
// Also, why not treat "CommandEcho" as an actual variable instead?
print( vec[0] + " = " + BoolStr(getCommandEcho) );
return;
}
if( vec[0] == "setCommandEcho" ){
// You had a logical error here; it would reject the argument if it
// was not "true" OR if it was not "false", so to be accepted, it would
// have to be both, which is impossible.
if( vec.size() != 2 || (vec[1] != "true" && vec[1] != "false") ){
print( "Error in arguments." );
return;
}
// Call setCommandEcho
setCommandEcho( StrBool(vec[1]) );
}
};

Share this post


Link to post
Share on other sites
I would then suggest to either use functors, or to use something like:


class BaseWhateverResource {
//does strictly nothing
};

template<typename T>
class Resource : public BaseWhateverResource {
public:
T contents;
Resource( T & o ) contents( o ) { }

//Add some syntactic sugar if you wish, for "->", "*" or others
};





Then, you could simply use :


typedef FunctionPointerType Whatever;
BaseWhateverResource * resource = get( );

//The next line is, as any cast such cast, dangerous!
Whatever ptr = ((Resource<Whatever>*) resource )->contents;


(What this effectively does is "hide" the pointer to a function inside a normal pointer, which can then be passed to whatever you want just like any other pointer).

Share this post


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

  • Advertisement