I'm stuck in fixing this gcc warning : I got tree version of the method "registerCalBack", each of them takes a different "callable" introduced through std::function. Depending on various callable type I declare, I can compile or not, gcc issuing a warning "call of overloaded registerCallBackxxxxx is ambiguous".
I know overloading can be resolved by compiler considering arguments, and not return type, but in that case I failed to understand why gcc is seeing ambiguity : to me, each TCallBack... I defined are different in their argument, and when I change return type of the third one, it compiles... this is really confusing me. I guess part of the problem comes from the fact some parameters are actually incomplete type, but this is how they are accessible from SDL headers, so I reproduced it the example I provide in this thread.
In comment in the code you got examples of definitions that compiled and other that not.
I hope some of you will understand better than me, right now I do not know where to look. Many thanks in advance.
here the gcc command line to compile:
-pedantic -W -Wall -Wextra -std=c++2a -Weffc++ -Wfatal-errors -Winit-self -Wnon-virtual-dtor -Winline -Wmissing-declarations -Wunreachable-code -Wshadow -Wswitch-enum -fstack-protector -Wstack-protector -O0
P.
#include <iostream>
#include <functional>
//This is how SDL_Renderer and SDL_Texture are declared in SDL.h, as incomplete type declaration, to make it opaque
//I reproduce it here with other name, to avoid the need to install SDL if you want to test
struct RENDERER;
typedef struct RENDERER RENDERER;
struct TEXTURE;
typedef struct TEXTURE TEXTURE;
//this is stupid, just to make test
struct dumb;
typedef struct dumb dumb;
class ClassUsingCallBacks // an instance of this class will use callbacks
{
public:
typedef std::function < RENDERER* (void) > TCallBack_GetRenderer;
typedef std::function < TEXTURE* (const std::string&) > TCallBack_GetTexture;
//this works:
// typedef std::function < dumb* (void) >
// typedef std::function < dumb* (TEXTURE*) >
// typedef std::function < int (TEXTURE*) >
// typedef std::function < TEXTURE* (TEXTURE*) >
// BUT THIS FAILED TO COMPILE :
// typdef std::function < void (TEXTURE*) >
// typdef std::function < void* (TEXTURE*) >
// typedef std::function < void (const std::string&, int, int, int)
typedef std::function < void (TEXTURE*) > TCallBack_removeTexture;
virtual ~ClassUsingCallBacks() {};
void registerCallBack(TCallBack_GetRenderer cb) {
std::cout << "Register a TCallBack_GetRenderer" << std::endl;
getRenderer = cb;
}
void registerCallBack(TCallBack_GetTexture cb) {
std::cout << "Register a TCallBack_GetTexture" << std::endl;
getTexture = cb;
}
void registerCallBack(TCallBack_removeTexture cb) {
std::cout << "Register a TCallBack_removeTexture" << std::endl;
removeTexture = cb;
}
//to test registered callbacks
void makeCalls(void) {
if (getRenderer) getRenderer();
if (getTexture) getTexture("a name");
//not this one since it's the one we failed to implement :/
// if (removeTexture) removeTexture();
}
protected:
TCallBack_GetRenderer getRenderer {};
TCallBack_GetTexture getTexture {};
TCallBack_removeTexture removeTexture {};
};
class ClassWithCallBacks
{
public:
virtual ~ClassWithCallBacks() {};
RENDERER* getRenderer(void) {
std::cout << "Inside getRenderer" << std::endl;
return nullptr;
}
TEXTURE* getTexture(const std::string& s) {
(void)s;
std::cout << "Inside getTexture" << std::endl;
return nullptr;
}
void removeTexture(TEXTURE* t) {
(void)t;
std::cout << "Inside removeTexture" << std::endl;
}
};
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
std::cout << "entering main" << std::endl;
ClassWithCallBacks calledObject;
ClassUsingCallBacks user;
auto cb_1 = std::bind(&ClassWithCallBacks::getRenderer, calledObject);
user.registerCallBack(cb_1);
auto cb_2 = std::bind(&ClassWithCallBacks::getTexture, calledObject, std::placeholders::_1);
user.registerCallBack(cb_2);
user.makeCalls();
std::cout << "Leaving main" << std::endl;
return 0;
}
std::bindproduces a callable object that takes arbitrary number of arguments, and simply discards those that don't need to be forwarded to the bound callable.std::function<void(Something)>accepts a callable that returns a result, then simply discards this result.Therefore,
cb_1can be accepted by bothstd::function<RENDERER* (void)>andstd::function<void (TEXTURE*)>. It can take (and ignore)TEXTURE*parameter, and the function can ignore its return value.Fundamentally, you are relying heavily on type erasure, but then hoping that the types you are erasing would nevertheless help guide overload resolution. Personally, I would have given the three
registerXXXfunctions different names, reflecting the kind of callback they are registering. No overloading, no problem.