#include <algorithm>
#include <functional>
#include <iomanip>
#include <iostream>
#include <memory>
#include <vector>

using std::size_t;

struct Tested;


class CallChecker
{
public:
	typedef void (Tested::*Call)();
	
	template <class T>
	struct type_identity { typedef T type; };
	template <class T>
	using type = typename type_identity<T>::type;  //used to prevent deduction

	template <class R, class... A>
	void addCheck(R (Tested::*call)(A...), type<std::function<bool (A...)>> check)
	{
		auto key = reinterpret_cast<Call>(call);
		mChecks.emplace_back(key, std::make_shared<CheckImpl<A...>>(std::move(check)));
	}
	
	template <class R, class... A>
	bool checkCall(R (Tested::*call)(A...), type<A>... arg)
	{
		auto key = reinterpret_cast<Call>(call);
		auto hasKey = [key](const std::pair<Call, std::shared_ptr<Check>> &p)
			{ return p.first == key; };
		for (
			auto it = std::find_if(begin(mChecks), end(mChecks), hasKey);
			it != end(mChecks);
			it = std::find_if(++it, end(mChecks), hasKey)
		)
		{
			auto *check = static_cast<CheckImpl<A...>*>(it->second.get());
			if (check->mCheck(arg...))
				return true;
		}
		return false;
	}

private:
	struct Check
	{
		virtual ~Check() {}
	};
	
	template <class... A>
	struct CheckImpl : Check
	{
		std::function<bool (A...)> mCheck;
		
		CheckImpl(std::function<bool  (A...)> check) : mCheck(std::move(check)) {}
	};

	std::vector<std::pair<Call, std::shared_ptr<Check>>> mChecks;
};


struct Tested : public CallChecker
{
	void foo() { std::cout << "foo: " << checkCall(&Tested::foo) << '\n'; }
	
	void bar(std::string s, int i) { std::cout << "bar: " << checkCall(&Tested::bar, std::move(s), i) << '\n'; }
};

int main() {
	std::cout << std::boolalpha;
	Tested test;
	test.addCheck(&Tested::foo, [] { return true; });
	test.foo();
	test.bar("adams", 42);
	test.addCheck(&Tested::bar, [](std::string s, int i) { return s == "adams" && i == 42; });
	test.bar("adams", 42);
	test.bar("douglas", 42);
	return 0;
}