Question

I'm working on a 2D game engine and I continuosly run into template problems. So, for this one, I've got a templated function like this:

template <class T>
T *S2M_CreateObject(int x, int y) {
    return new T(x, y);
}

now, I would like the game to load the level data from a file, and that includes loading and instantiating Object-derived classes, so I made an std::map like this:

map <string, Object *(*)(int x, int y)> o {
    { "warp", &S2M_CreateObject<Warp> }
};

which stores a string that I will be using in the level editor to refer a determined class and maps it to a function pointer that would create an instance of that said class.

I hope you get the idea, this is the approach I like the most but it is not working. However, it works if I delete the Warp template specifier (Warp is a derived class of Object), but that is not the goal. I know I could create a function for every object type I have defined in the game, but since I'm programming a game engine, I can't figure out how many Object-derived classes the user will create and I cannot expect him/her to program each function.

Any other way I can do this?

Was it helpful?

Solution

Whereas Warp* can be implicitly converted to Object*, a pointer to a function returning Warp* cannot be implicitly converted to a pointer to a function returning Object*. Nor, in general, can such a conversion be performed safely at all.

Now the reason why your code doesn't work should be clear. &S2M_CreateObject<Warp> has type Warp* (*)(int, int), and this can't be implicitly converted to Object* (*)(int, int). Instead, you can make the S2M_CreateObject function always return Object* regardless of which type is actually created:

#include <map>
#include <string>
using namespace std;

struct Object {
    Object() {}
    Object(int x, int y) {}
};
struct Warp : Object {
    Warp() {}
    Warp(int x, int y) {}
};

template <class T>
Object* S2M_CreateObject(int x, int y) {
    return new T(x, y);
}

int main() {
    map<string, Object *(*)(int x, int y)> o {
        { "warp", &S2M_CreateObject<Warp> }
    };
}

OTHER TIPS

Thanks to your previous help, I could do something like this: here is somehow the working result (simplified). I used it with a quite simple range function

//Define your template function
template<typename Type>
void fnRangeValue(CMyClass * poMyObject, std::string strFormat){
    Type tMyField, tMinValue, tMaxValue;
    /*Do what you have to here!*/
}

//Define a macro for your pointerFunction
typedef void (*fnPointerFunctionRange)(CMyClass * poMyObject, std::string strFormat );
// Define your lookup table (map)
const std::map<std::string, fnPointerFunctionRange> ST_FORMAT_RANGE_POINTER= {
        {"UINT8",&fnRangeValue<uint8_t>},
        {"STR1UINT8",&fnRangeValue<uint8_t>},
        {"UINT16",&fnRangeValue<uint16_t>},
        {"STR2UINT16",&fnRangeValue<uint16_t>},
        {"STR4UINT16",&fnRangeValue<uint16_t>},
        {"UINT32",&fnRangeValue<uint32_t>},
        {"INT8",&fnRangeValue<int8_t>},
        {"INT16",&fnRangeValue<int16_t>},
        {"STR3INT16",&fnRangeValue<int16_t>},
        {"INT32",&fnRangeValue<int32_t>},
        {"FLT32",&fnRangeValue<float>},
        {"FLT64",&fnRangeValue<double>},
        {"STR7FL64",&fnRangeValue<double>},
        {"STR8FL64",&fnRangeValue<double>},
};

void fnRangeField(CMyClass * poMyObject){
    std::string strFormat;
    fnPointerFunctionRange poFonctionRange;
    strFormat = "UINT8";
    auto itMapRangePointer = ST_EOIIM_FORMAT_RANGE_POINTER.find(strFormat);
    if(itMapRangePointer != ST_FORMAT_RANGE_POINTER.end()){
        poFonctionRange = ST_FORMAT_RANGE_POINTER.at(strFormat);
        // Call of the right template function thanks to pointers
        poFonctionRange(poMyObject,strFormat);
    }
}

Hope it will help you!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top