Sign in to follow this  
garyfletcher

Creating std::map of templates

Recommended Posts

garyfletcher    250
Hi all The problem, apologise now if this is too long, please bear with it. I have a stack that contains pointers to my Class member functions:
boost::function < bool (myClass*) > funcPtr;
typedef void (myClass::*StatePMF)();

struct FuncStruct
{
   string funcName;
   StatePMF funcPtr;
};

std::stack <FuncStruct> mFuncStack;



These functions are called from with a loop from a dll:
while(!mFuncStack.empty())
{
   mExecuteFunc(mFuncStack.top().funcPtr);
}

void mExecuteFunc(StatePMF funcPtr)
{
  (this->*funcPtr)();
}



A typical function on the top of the stack would look like:
void SiSE::mCallingFunc(void)
{
    if (!mExecutionFunc(this)) // See below for this variable ******
    {
        throw SiSEException("mExecutionFunc() Failure");    
    }
       
    return;    
}



This works fine it executes the member function at the top of the stack. In turn this the member function calls a function in a project that has been registered with myClass. The project functions must return a bool and take a pointer to myClass as an argument such as. I use the boost::function<> library to do this:
#include <boost/function.hpp>

typedef boost::function<bool (SiSE*)> funcPtr;
typedef std::pair<int, funcPtr> mfuncPair;
typedef std::map <int, funcPtr> funcMap;
funcMap mFuncMap;
funcPtr mExecutionFunc; //******* Here it is



I register the function with myClass and and store the registered function in a map with a unique integer value. The key value is returned to the deployment project and is then used to reference the function. In order to call the function the mExecutionFunc is set to the value in the map as indicated by the passed key argument.
int myClass::mRegFunction(funcPtr aFuncPtr)
{
    // Increment the function id
    ++mFuncID;
    
    // See if it's in the function map
    if (!mCheckFuncList(mFuncID))
    {
        // It isn't, so add the function and the new key
        mFuncMap.insert(mfuncPair(mFuncID,aFuncPtr));
    }
    else
    {
        // It is. return a value that isn't a funcID.
        return -1;   
    }
    
    // Everything's OKAY so return the key.
    return mFuncID;   
}

bool myClass::mSetExecutionFun(int aFuncID)
{
    funcMap::iterator FMapIter;
    
    // See if it's in the function map
    // return true if it is and populate the iterator
    // otherwise return false
    if (mCheckFuncList(aFuncID,FMapIter))
    {
        // Found it. Set the execution function
        mExecutionFunc = FMapIter->second;
    }
    else
    {
        // It is. ERROR.
        std::cerr << "ERROR: Function id <" << aFuncID << "> does not exist in 
                      function map. Cannot set execution function" << std::endl;
        return false;   
    }
    
    // Everything's OKAY so return true.
    return true;
    
}

// Overloaded mCheckFuncList()
bool myClass::mCheckFuncList(int aFuncID, funcMap::iterator& FMapIter)
{
    // Check that this function ID is in our function list
    FMapIter = mFuncMap.find(aFuncID);
    
    if (FMapIter == mFuncMap.end())
    {
        return false;
    }
    
    return true;
    
}

bool myClass::mCheckFuncList(int aFuncID)
{
    // Check that this function ID is in our function list
    funcMap::iterator FMapIter = mFuncMap.find(aFuncID);
    
    if (FMapIter == mFuncMap.end())
    {
        return false;
    }
    
    return true;
    
}



I find this limitaion very restricting and so want to find a way to associated arguments with the functions. Currently I have a maps declared as
typedef std::pair<std::string, std::string> argNVPair;
typedef std::map<std::string, std::string> argNV;
typedef std::pair<int, argNV> argMapPair;
typedef std::map<int, argNV> argMap;

argMap mArgMap;
argNV mArgNV;



So the arguments are stored in maps with a std::string key and an std::string value that are then stored in another map with the funcID to associate the argument with a particular function . This means that I can store simple arguments, ints, floats, std::strings etc., but what if I wanted to more complex data structures like structs, classes etc. I have various utility functions that currently provide functionality to store argumenst in teh current list but I won't include those as they follow the same basic pattern as the ones included. The only way I can think of doing this is to provide templated functions and classes. I would still like to keep the basic design pattern of storing arguments in std::map<> templates but seem to remember that I cannot store template classes in a std::map<>. Can I create a class that has a member that is itself a template class and has tempate functions to provide adding and retrieval methods, and then store that in a std::map or is there a much simpler method? Sorry about the length of this post...hope you got all the way through it.

Share this post


Link to post
Share on other sites
Fruny    1658
If you have a fixed set of parameter types, have a look at boost::variant instead of boost::any. I'd posted a "variadic function" wrapper some weeks ago, I'm sure somebody has kept a link. If not, I'll paste my code again sometime...

Share this post


Link to post
Share on other sites
garyfletcher    250
Taken a look and I can't use a boost::variant<> in a std::map as it's a template class...boohiss!!!!

The only way I can think of doing it, to include complex data structures is to use a void*. Not pretty at all.

EDIT:

Hang on a mo can I just:

typedef boost::variant <int, std::string> argTypes;
std::map(std::string, argTypes> argMap;

Share this post


Link to post
Share on other sites
garyfletcher    250
Okay then I've got

typedef boost::variant <int, std::string> argTypes;
std::map(std::string, argTypes> argMap;


Now I'm trying to extract the info at runtime but I'm not sure about how to get at the type I want.

If I knew I had an int I would use

int anInt
anInt = boost::get<int>(argMap->second);


But I don't know the type of the value I'll need. I can make an if statement like this:


#include<typeinfo>

mGetArgValue(argTypes& anArg)
{
if (typeid(anArg) == typeid(int))
anArg = boost::get<int>(argMap->second);
else if (typeid(anArd) == typeid(std::string))
anArg = boost::get<std::string>(argMap->second);


But Mr. Stroustrup says this is BAD!!!!

Any suggestions?

Share this post


Link to post
Share on other sites
Fruny    1658
Quote:
Original post by garyfletcher
lol....what's a "variadic function"?


A function that can take a variable number of parameters, such as the ellipsis (...) lets you do in C.

Quote:
if it'll help with my problem I'd love to take a look if anyone can help.


You'll have to wait until tomorrow. PM me if you're still interested.

Share this post


Link to post
Share on other sites
garyfletcher    250
I see..a variable argument list.

Okay. Am having another problem using the boost::variant<> if anyone's interested.

Have got

typedef boost::variant <int, std::string> argTypes;
void aFunc(int anInt, std::string& aString, argType& anArg)
{
// some stuff
}


But the compiler seems to objejct when I try to call it. Is the method I'm attempting legal? Does anyone know?

Share this post


Link to post
Share on other sites
Fruny    1658
Ok, actually I've found the right link. I've been working a bit more on that class (I really need to finish it), but that should give you an idea.

http://www.gamedev.net/community/forums/viewreply.asp?ID=2069073

The factory function probes the function pointer you pass it to find the number and types of parameters it expects, then return a boost::function object that accepts a std::vector of boost::variant. When actually doing the call, the function object will check that the number and type of the parameters stored in the vector are correct (throws an exception if not) and then extracts them and passes them to the function.

Although it accepts a vector, it only supports a finite number of parameters (the function-probing code only goes so far), but evil Boost preprocessor trickery allows you to set that limit at will by redefining FNWRAP_MAX_ARITY

Again, note that this isn't production code - I have a more mature version, but even that note needs some further polishing (I'm probably going to have to hijack boost::function's internals).

Share this post


Link to post
Share on other sites
Fruny    1658
Quote:
But the compiler seems to objejct when I try to call it. Is the method I'm attempting legal? Does anyone know?


What error do you get?

Share this post


Link to post
Share on other sites
garyfletcher    250
The compiler was complaining about the argTypes tydef. it was lookign for a matching function but couldn't find one. The error messages had expanded the variant type def

Ah...I've made mods to the code to try to use a template function to pass in arguments of any type and then add it to the map. Is still complaining though:


template <class aType, class bType>
bool myClass::mRegArgument(int aFuncID, aType& argName, bType& argValue)
{
// stuff
}

called with:

if(!GE->mRegArgument(funcID,arg1,50))
{
std::cout << "Unable to register arg1" << std::endl;
}


Error:

SiSE2DDev.cpp:234: error: no matching function for call to `SiSE::mRegArgument(int&, std::string&, int)'
C:/SiSE/include/SiSE.h:126: note: candidates are: bool SiSE::mRegArgument(int, aType&, bType&) [with aType = std::string, bType = int]

Any ideas?

[Edited by - garyfletcher on June 30, 2005 1:00:06 AM]

Share this post


Link to post
Share on other sites
garyfletcher    250
I know why now. I was passing 50, an absolute value, in but the template function was expecting something it could reference...doh. It's late.

[Edited by - garyfletcher on June 30, 2005 1:11:56 AM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this