Matthias
Matthias

Reputation: 10379

How to correctly combine type aliases, function pointers, and template functions

I have the following class declared in my .h file:

class BeliefCondFunc
{
private:
    using funcTypeBool = bool(*)(const bool&, const std::list<bool>&, const std::vector<bool>&);
    using funcTypeInt = bool(*)(const int&, const std::list<int>&, const std::vector<int>&);
    using funcTypeFloat = bool(*)(const float&, const std::list<float>&, const std::vector<float>&);
    using funcTypeString = bool(*)(const std::string&, const std::list<std::string>&, const std::vector<std::string>&);

    static std::unordered_map<std::string, funcTypeBool> const m_FunctionMapBool;
    static std::unordered_map<std::string, funcTypeInt> const m_FunctionMapInt;
    static std::unordered_map<std::string, funcTypeFloat> const m_FunctionMapFloat;
    static std::unordered_map<std::string, funcTypeString> const m_FunctionMapString;

    template <typename T>
    static bool Greater(const T& Fact, const std::list<T>& Facts, const std::vector<T>& Params)
    {
        return Params.empty() ? false : (Fact > Params[0]);
    }

public:
    template <typename T>
    static bool Call(const std::string FuncName, const T& Fact, const std::list<T>& Facts, const std::vector<T>& Params)
    {
        if(typeid(T) == typeid(int))
            return m_FunctionMapInt.find(FuncName) != m_FunctionMapInt.end() ? (*m_FunctionMapInt.at(FuncName))(Fact, Facts, Params) : false;

        return false;
    }
};

And in the .cpp file I define the const function mappings like so:

std::unordered_map<std::string, BeliefCondFunc::funcTypeInt> const BeliefCondFunc::m_FunctionMapInt
{
    { "Greater", &Greater<int> },
};

However, when I try to compile this code I get the following error regarding the call to the function pointer in the Call() method:

error C2664: 'bool (const int &,const std::list<int,std::allocator<_Ty>> &,const std::vector<_Ty,std::allocator<_Ty>> &)': cannot convert argument 3 from 'const std::vector<std::string,std::allocator<_Ty>>' to 'const std::vector<int,std::allocator<_Ty>> &'*

Any ideas what I am doing wrong here?

Upvotes: 0

Views: 87

Answers (2)

MikeMB
MikeMB

Reputation: 21156

As Mikel F mentioned, the problem is that the part in the body of the if statement has to compile, even if it is never called. That statement is (conceptually) a runtime check, so both branches have to be valid at compile time.

To solve this problem (as long as you don't have a c++17 compiler available), you can write two versions:

  • the general templated function that takes any T and always returns false
  • A specialized version for T==int that will get called for ints and uses the formular in the body of your if statement:

Example*:

template <typename T>
static bool Call(const std::string FuncName, const T& Fact, const std::list<T>& Facts, const std::vector<T>& Params)
{
    return false;
}

template <>
static bool Call<int>(const std::string FuncName, const int& Fact, const std::list<int>& Facts, const std::vector<int>& Params)
{
    return m_FunctionMapInt.find(FuncName) != m_FunctionMapInt.end() ? (*m_FunctionMapInt.at(FuncName))(Fact, Facts, Params) : false;
}

*) I didn't put the code through a compiler, so it might contain typos.

Upvotes: 4

Mikel F
Mikel F

Reputation: 3656

You need constexpr if. when you instantiate your function for each type, it still tries to create the code for every statement in the template, even if it is not meant for each type. For std::string:

 if(typeid(std::string) == typeid(int))
            return m_FunctionMapInt.find(FuncName) != m_FunctionMapInt.end() ? (*m_FunctionMapInt.at(FuncName))(Fact, Facts, Params) : false;

Which results in your error because you can't pass a std::string to the integer map function.

Upvotes: 1

Related Questions