Search code examples
c++operator-overloadingfunctortype-erasure

function object with overloaded type conversion operator crashes


I'm implementing a type erasure class (I'm studying templates and static polymorphism), that can wrap any callable object (any object that supports the function call syntax).

Reading C++ template complete guide and searching across the web, I came up with this implementation:

// erasure.hpp 

class Object
{
friend void swap(Object &, Object&);  // friend declaration
private:
    class Concept
    {
    public:
        virtual ~Concept() = default;
        virtual void Invoke() = 0;
        virtual Concept *Clone() = 0;
    protected:
        Concept() = default;
    };
    template <typename T>
    class Model : public Concept
    {
    public:
        //Model(const T&) : mObject(object) {}
        //Model(T&&) : mObject(move(object)) {}
        template <typename U>                          // forwarding constructor (forwarding (universal) reference)
        Model(U &&object) : mObject(forward<U>(object)) {}    
        void Invoke() override { mObject(); }
        Model *Clone() override;
    private:
        T mObject;
    };
public:
    template <typename T>                              // forwarding constructor (forwarding (universal) reference)
    Object(T &&object) : mConcept(new Model<typename remove_reference<T>::type>(forward<T>(object))) {}  
    Object(const Object &other) : mConcept(other.mConcept->Clone()) {}               // copy constructor
    Object(Object &other) : Object(static_cast<const Object&>(other)) {}             // delegating constructor (inhibits forwarding constructor)
    Object(Object &&other) : mConcept(other.mConcept) { other.mConcept = nullptr; }  // move constructor
    ~Object() {delete mConcept; }                 // destructor
    template <typename T>                         // forwarding assignment operator
    Object &operator=(T&&);
    Object &operator=(const Object&);             // copy assignment operator
    Object &operator=(Object &other) { return *this = static_cast<const Object&>(other); }
    //Object &operator=(Object &other) { return this->operator=(static_cast<const Object&>(other)); }
    Object &operator=(Object&&);                  // move assignment operator
    void operator()();
private:
    Concept *mConcept;
};

template <typename T>
Object::Model<T> *Object::Model<T>::Clone()
{
    return new Model(mObject);
}

template <typename T>
Object &Object::operator=(T &&object)
{
    delete mConcept;  
    mConcept = new Model<remove_reference_t<T>>(forward<T>(object));

    return *this;
}

this is pretty classic and straightforward, so I omit the cpp implementation file. It works fine when I construct type erased objects from free functions, functors that overload the function call operator and even lambdas:

#include "erasure.hpp"
#include <iostream>

#define PRINT(msg)       std::cout << (msg)
#define PRINTLN(msg)     PRINT(msg) << std::endl;

void FreeFunction()
{
    PRINTLN("in free function");
}

class Functor1
{
public:
    void operator()()
    {
        PRINTLN("in functor overloaded function call operator");
    }
};

class Functor2
{
private:
    typedef void(*PtrToFun)();
public:
    operator PtrToFun()
    {
        PRINTLN("in functor overloaded type conversion operator to function pointer");
    }
};

int main(int argc, char **argv)
{
    Object o1 = &FreeFunction;
    Object o2 = Functor1();
    Object o3 = Functor2();
    Object o4 = []() -> void { PRINTLN("in lambda"); };

    o1();
    o2();
    o4();
    o3();  // error: program crashes

    return 0;
}

but when I use a functor (Functor2) that overloads a type conversion operator to function pointer, the program crashes AFTER printing the string inside the operator. It just freezes and crashes (stack overflow?).

Why?

EDIT

after comment by rafix07 I edited the code and now it works:

class Functor2
{
private:
    typedef void(*PtrToFun)();
    PtrToFun p = []() { PRINTLN("in functor overloaded type conversion operator to function pointer"); };
public:
    operator PtrToFun()
    {
        return p;
    }
};

Solution

  • Your Functor2 is just wrapper for pointer to function, so it should take some pointer and return it in conversion operator. Without return, you have UB because of calling function on garbage address.

    To make Functor2 take any function with signature void () you could write:

    class Functor2
    {
    private:
        typedef void(*PtrToFun)();
        PtrToFun ptr;
    public:
        Functor2(PtrToFun p ) : ptr(p) {}
    
        operator PtrToFun()
        {
            PRINTLN("in functor overloaded type conversion operator to function pointer");
            return ptr;
        }
    };
    
    Object o3 = Functor2(&FreeFunction);
    o3();