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;
}
};
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();