I've implemented a simple any
class and an appropriate visitor
class. This works great on function level. But when I try to use the visitor as a member within a class the registered member function is found, called but not entered.
That's a strange behavior that I don't understand.
I tried it with gcc 5 and 6.
You will find my code below and here online.
The program should increment an integer value.
#include <iostream>
#include <functional>
#include <typeindex>
#include <unordered_map>
// simple any class
struct any {
~any() { if(val) { deleter_(val); } }
template < class T >
any(const T &v)
: val(new T(v))
, deleter_(&destroy<T>)
, indexer_(&index<T>)
{}
typedef void (*deleter)(void*);
typedef std::type_index (*indexer)();
template <typename T>
static void destroy(void *p) { delete (T*)p; }
template < class T >
static std::type_index index() { return std::type_index(typeid(T)); }
template < class T >
T& as() { return *static_cast<T*>(val); }
std::type_index type_index() const { return indexer_(); }
void *val = nullptr;
deleter deleter_ = nullptr;
indexer indexer_ = nullptr;
};
struct any_visitor {
using function = std::function<void(any&)>;
template <typename T>
void register_visitor(const std::function<void(T&)> &f) {
fs.insert(std::make_pair(
std::type_index(typeid(T)),
function([&f](any & x) {
f(x.as<T>());
})
));
}
void visit(any & x) {
auto it = fs.find(x.type_index());
if (it != fs.end()) {
it->second(x);
}
}
std::unordered_map<std::type_index, function> fs;
};
struct processor {
// visitor as class member
any_visitor visitor;
processor() {
// register member function
visitor.register_visitor<int>([this](int &i) { this->process(i); });
}
void apply(any &a) { visitor.visit(a); }
void process(int &val) { ++val; }
};
int main()
{
any ai = 7;
any aii = 10;
// visitor inside a function
any_visitor visitor;
visitor.register_visitor<int>([](int &val) {
++val;
});
// increment works
std::cout << "function before: " << aii.as<int>() << "\n";
visitor.visit(aii);
std::cout << "function after: " << aii.as<int>() << "\n";
// increment doesn't work
std::cout << "class before: " << ai.as<int>() << "\n";
processor p;
p.apply(ai);
std::cout << "class after: " << ai.as<int>() << "\n";
return 0;
}
You're storing a reference to a temporary, with all that entails of undefined behaviour.
The lambda function in register_visitor
needs to capture f
by value.