Search code examples
c++c++11templatessmart-pointers

Cannot pass shared_ptr argument to function pointer


I have a problem with the code that uses function pointer that takes shared_ptr argument.

Here is a sample code.

header.h

#include <functional>
#include <iostream>
template <class T> class FuncWrapper{
    private:
        void (*original_function)(T a);
    public:
        void setFunction(void *func);
        void execFunction(T a, void *data);
};

template <class T> void FuncWrapper<T>::setFunction(void *func){
    original_function = (void (*)(T))func;
}

template <class T> void FuncWrapper<T>::execFunction(T a, void *data){   
    FuncWrapper<T>* wrapper = (FuncWrapper<T>*)data;
    std::cout << "inside wrapper " << *(a.get()) << std::endl;
    wrapper->original_function(a);
}

main.cpp

#include <iostream>
#include <memory>
#include "header.h"

class ClassA{
    public:
        ClassA(std::shared_ptr<int> a){
            FuncWrapper<std::shared_ptr<int>> *fw;
            fw = new FuncWrapper<std::shared_ptr<int>>;
            fw->setFunction((void*)&ClassA::print_int);
            std::function<void(std::shared_ptr<int>)> g = std::bind(&FuncWrapper<std::shared_ptr<int>>::execFunction, fw, std::placeholders::_1, fw);
            g(a);
            delete fw;
        }
    private:
        void print_int(std::shared_ptr<int> x) {
            std::cout << "printing int" << std::endl;
            std::cout << "given int " << *(x.get()) << std::endl;
        }
};

int main(int argc, char * argv[]){
    std::shared_ptr<int> x = std::make_shared<int>(10);
    std::cout << "inside main " << *(x.get()) << std::endl;
    ClassA *temp;
    temp = new ClassA(x);
    delete temp;
    return 0;
}

Result

inside main 10
inside wrapper 10
printing int
Segmentation fault (core dumped)

I cannot figure out why it causes segmentation fault.

Changing std::shared_ptr<int> to int works just fine.
Therefore I assume that it has to do with owenership of shared_ptr, but I'm not familiar with smart pointers and I'm completely at loss.

I want to know

  • why it does not work
  • how to make it work

Limitations are

  • w/o changing print_int function itself
  • execute function within FuncWrapper<T>::execFunction
  • FuncWrapper<T>::execFunction has to be static

Otherwise, it is free to change. (inside ClassA constructor, inside main execFunction etc.)


Solution

  • The problem is not the shared_ptr, but mismatch between pointers to a function and a member function.

    Your function wrapper expects a pointer to a function (void (*)(std::shared_ptr<int>)), but you provide a pointer to a member function (void (ClassA::*)(std::shared_ptr<int>)), which is different. An implicit leading argument of pointer to this is added to it.

    This is how it really looks like:

    // pointer to member function
    void (*)(ClassA *ptr, std::shared_ptr<int>)
    

    Your shared_ptr goes to the first argument and fortunately the application segfaults.

    One of the solutions is to make the function print_int static.

    class ClassA{
        public:
            ClassA(std::shared_ptr<int> a){
                FuncWrapper<std::shared_ptr<int>> *fw;
                fw = new FuncWrapper<std::shared_ptr<int>>;
                fw->setFunction((void*)&ClassA::print_int);
                std::function<void(std::shared_ptr<int>)> g = std::bind(&FuncWrapper<std::shared_ptr<int>>::execFunction, fw, std::placeholders::_1, fw);
                g(a);
                delete fw;
            }
    
        private:
            static void print_int(std::shared_ptr<int> x) {
                std::cout << "printing int" << std::endl;
                std::cout << "given int " << *(x.get()) << std::endl;
            }
    };
    

    But there seems to be another problem in your code. Function pointers should not be converted to object pointers (which void * is). Maybe change your setFunction this way:

    void setFunction(void (*func)(T)) {
            original_function = func;
    }
    

    More on that here