#include <iostream>
#include <type_traits>
#include <functional>


template <typename T>
using GetBaseType = typename T::Base;

using NoBase = void;

template <typename T>
class CallUp;

template <>
class CallUp<NoBase>
{
public:
   template <typename Op, typename T>
   CallUp(Op&& ignoreOperation, T&& ignoreObject) {}
};


template <typename T>
class CallUp : public CallUp<GetBaseType<T>>
{
public:
 	template <typename Op>
	CallUp(Op&& op, T& obj) : CallUp<GetBaseType<T>>(op, obj), op(std::forward<Op>(op)), obj(obj)
	{}
	~CallUp()
	{
		op(obj);
	}
private:
	std::function<void(T&)> op;
	T& obj;
};


template <typename T>
class ConstCallUp;

template <>
class ConstCallUp<NoBase>
{
public:
   template <typename Op, typename T>
   ConstCallUp(Op&& ignoreOperation, T&& ignoreObject) {}
};

template <typename T>
class ConstCallUp : public ConstCallUp<GetBaseType<T>>
{
public:
 	template <typename Op>
	ConstCallUp(Op&& op, const T& obj) : ConstCallUp<GetBaseType<T>>(op, obj), op(std::forward<Op>(op)), obj(obj)
	{}
	~ConstCallUp()
	{
		op(obj);
	}
private:
	std::function<void(T&)> op;
	const T& obj;
};

template <typename ...T>
struct MultiBases {};

template <typename ...T>
class CallUp<MultiBases<T...>> : public CallUp<T>...
{
public:
 	template <typename Derived, typename Op>
	CallUp(Op&& op, Derived& obj) : CallUp<T>(op, obj)...
	{}
};
template <typename ...T>
class ConstCallUp<MultiBases<T...>> : public ConstCallUp<T>...
{
public:
 	template <typename Derived, typename Op>
	ConstCallUp(Op&& op, Derived& obj) : ConstCallUp<T>(op, obj)...
	{}
};

template <typename Op, typename T>
auto callUp(Op&& op, T& obj)
{
	return CallUp<T>(std::forward<Op>(op), obj);
}
template <typename Op, typename T>
auto callUp(Op&& op, const T& obj)
{
	return ConstCallUp<T>(std::forward<Op>(op), obj);
}

class Foo01
{
public:
	using Base = NoBase;
	virtual void reset()
	{
		std::cout << "reset Foo01\n";
	}
	void print() const
	{
		std::cout << "Foo01\n";
	}
};

class Foo02
{
public:
	using Base = NoBase;
	void reset()
	{
		std::cout << "reset Foo02\n";
	}
	void print() const
	{
		std::cout << "Foo02\n";
	}
};


class Foo1 : public Foo01, public Foo02
{
public:
	using Base = MultiBases<Foo01, Foo02>;
	void reset()
	{
		std::cout << "reset Foo1\n";
	}
	void print() const
	{
		std::cout << "Foo1\n";
	}
};

class Foo2 : public Foo1
{
public:
	using Base = Foo1;
	void reset()
	{
		std::cout << "reset Foo2\n";
	}
	void print() const
	{
		std::cout << "Foo2\n";
	}
};

template <typename T>
using object_class_t = std::remove_cv_t<std::remove_reference_t<T>>;

int main() {
	Foo2 foo2;
	callUp([](auto&& obj){ using ObjClass = object_class_t<decltype(obj)>; obj.ObjClass::reset();}, foo2);
	callUp([](auto&& obj){obj.print();}, foo2);
}