Makogan
Makogan

Reputation: 9536

How can you pass a function pointer and compare it in a function in C++?

For testing reasons I have a certain number of methods whose signature in C++ would look like:

dvec3 (*)(dvec3, dvec3, double)

I put them in a vector as follows:

vector<dvec3 (*)(dvec3, dvec3, double)> methods = {lerp, plerp, splerp, ilerp};

The idea is to make a function that takes the function pointer and returns a string to identify the function that is currently being used (i.e I want to print out which function, among the 4 above, is being used)

For this I attempted to write the method as follows (I ommitted most cases on purpose):

string inline erpToString(dvec3 (*f)(dvec3, dvec3, double))
{
    if (f==lerp)
    {
        return "Lerp";
    }
}

The above however does not compile, the error message states that it's a casting error. What did I do wrong?

EDIT:

Compiler message:

/home/kronos/Desktop/OpenGL-Template/source/Rendering/rendering.cpp: In function ‘std::__cxx11::string erpToString(glm::dvec3* (*)(glm::dvec3, glm::dvec3, double))’:
/home/kronos/Desktop/OpenGL-Template/source/Rendering/rendering.cpp:1362:7: error: invalid operands of types ‘glm::dvec3* (*)(glm::dvec3, glm::dvec3, double) {aka glm::tvec3<double, (glm::precision)0>* (*)(glm::tvec3<double, (glm::precision)0>, glm::tvec3<double, (glm::precision)0>, double)}’ and ‘<unresolved overloaded function type>’ to binary ‘operator==’
  if (f==lerp)
      ~^~~~~~
make[2]: *** [CMakeFiles/voxel-world.dir/build.make:111: CMakeFiles/voxel-world.dir/source/Rendering/rendering.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:68: CMakeFiles/voxel-world.dir/all] Error 2

EDIT:

I have made the minimum possible file that would have the same logic, however it does not reproduce the error:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <string>

using namespace std;
using namespace glm;

dvec3 lerp(dvec3 p1, dvec3 p2, double t)
{
    return (1-t)*p1+t*p2;
}

string inline erpToString(dvec3 (*f)(dvec3, dvec3, double))
{
    if (f==lerp)
    {
        return "Lerp";
    }
}

int main()
{

}

Playing around with the code shows the other methods (ilerp and company) do work. So it seems I have a namespace conflict (yay for using namespaces). SInce GLM defines a lerp function, but the one I need to use is the one I defined. Anyone got a suggestion?

Upvotes: 1

Views: 175

Answers (4)

JiaHao Xu
JiaHao Xu

Reputation: 2748

This is not an answer to how to fix the bug, but an answer to your attemp of giving a name of the method your are using.

I suggest you to use std::unordered_map to discriminate different functions.

std::unordered_map<void*, const char*> funcmap{4};

union cast_fptr {
    dvec3 (*)(dvec3, dvec3, double) fptr;
    void *void_ptr;
};
#define MAP_VAL(FUNC_NAME) std::pair<void*, const char*>{cast_fptr{.fptr = &FUNC_NAME}.void_ptr, #FUNC_NAME}
funcmap.insert({MAP_VAL(lerp), MAP_VAL(plerp), MAP_VAL(splerp), MAP_VAL(ilerp)});
#undef MAP_VAL

inline const char* erpToString(dvec3 (*f)(dvec3, dvec3, double)) noexcept {
    return funcmap[f];
}

Upvotes: 0

Makogan
Makogan

Reputation: 9536

The problem was caused because GLM defines a lerp Function as well, thus the compiler couldn't choose which function I was referring too.

The solution was, as suggested in the comments, renaming my function from lerp to mlerp

I guess this is a very good example as to why using namespaces is not a great idea.

Upvotes: 0

MSalters
MSalters

Reputation: 179819

The key is ‘<unresolved overloaded function type>’

In particular, you have two lerp functions. That's generally allowed. In most contexts, C++ will figure out which one you want. For instance, when you call lerp(arg1,arg2, arg3), C++ will do overload resolution with those three arguments. But in f==lerp, there are no three arguments for overload resolution. That is why C++ says "unresolved overload".

The solution is to cast lerp to the type of f: if (f==static_cast<decltype(f)>(&lerp)). The three arguments to use for overload resolution are those of f.

Upvotes: 0

Severin Pappadeux
Severin Pappadeux

Reputation: 20080

You could use using or typedef to simplify all thing. Code works for me

int a(float x, float y, double z)
{
    return int(x + y + z);
}

int b(float x, float y, double z)
{
    return int(x - y - z);
}


using fdef = int(*)(float, float, double); // or typedef int (*fdef)(float, float, double);

std::vector<fdef> v{a, b};

if (v[0] == a)
    std::cout << "qqq\n";

Upvotes: 3

Related Questions