Home > other >  How to use inheritance to form a generic callback function pointer?
How to use inheritance to form a generic callback function pointer?

Time:08-19

I am trying to write a function that would take in a generic function pointer as an argument. Suppose you have a class A and its derivatives like below

class A {
};
class A1: public A {
};
class A2: public A {
};
class A3: public A {
};

I have another function

void registerCallback(std::function<void(A*)> func_ptr) {
}

int main() {
   std::function<void(A1*)> fptr= [&](A1*) {
       cout << "dummy" <<endl;
   };
   registerCallback(fptr); /// throws error 
}

It tells you, cannot convert std::function<void(A*)> to std::function<void(A1*)> How can I solve this problem?

CodePudding user response:

How can I solve this problem?

You can use A* instead of A1* in the definition of fptr.

std::function<void(A1*)> fptr= [&](A*) {
    cout << "dummy" <<endl;
   };

It is instructive to understand why your code is erroneous, not just from a syntactic point of view but also from a semantic point of view.

From a syntactic point of view, A1* can be used where A* where is expected -- it is an automatic pointer conversion. However, std::function<void(A1*)> cannot be used where std::function<void(A*)> is expected. There is no automatic conversion.

It's more important to understand the semantic problem if that was not a syntactic error. Let's say for the sake of this discussion that the language/compiler accepted your code. Let's take the code a bit further. Update your code to the following:

class A {
};

class A1: public A {
    public:
        int int_var;
};

class A2: public A {
    public:
        double double_var;
};


class A3: public A {
    public:
        std::string string_var;
};

static std::function<void(A*)> registered_function_ptr;


void registerCallback(std::function<void(A*)> func_ptr) {
    registered_function_ptr = fun_ptr;
}


void callRegisteredFunction(A* a_ptr) {
    registered_function_ptr(a_ptr);
}

int main() {

    std::function<void(A1*)> fptr= [&](A1* a1_ptr) {
        // Expecting a pointer to an A1 object.
        // Should be able to use members of A1.
        cout << "int_var: " << a1_ptr->int_var << endl;
   };


   registerCallback(fptr);

    A2 a2;
    a2.double_var = 20;
    // Syntactically correct.
    callRegisteredFunction(&a2);

    A3 a3;
    a3.string_var = "Some string";
    // Also syntactically correct.
    callRegisteredFunction(&a3);
}

When callRegisteredFunction is executed, it calls the registered function. In this case it is fptr. fptr expects a pointer to an A1 object but we are able to call it indirectly with objects that are different -- they don't have int_var. Instead, they have double_var and string_var. That will definitely lead to undefined behavior. The compiler and the language are preventing you falling into that trap.

CodePudding user response:

Just declare fptr as a callable object with parameter of basic type - std::function<void(A*)>. It will still accept all classes (publicly) derived from A:

int main() {
    std::function<void(A*)> fptr= [&](A*) {
        std::cout << "dummy" << std::endl;
    };
    A1 *a = new A1{};
    
    fptr(a); // all good
}
  • Related