/*
	This file is part of solidity.
	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see .
*/
/**
 * Visitor templates.
 */
#pragma once
#include 
#include 
namespace dev
{
/// Generic visitor used as follows:
/// boost::apply_visitor(GenericVisitor(
///     [](Class1& _c) { _c.f(); },
///     [](Class2& _c) { _c.g(); }
/// ), variant);
/// This one does not have a fallback and will fail at
/// compile-time if you do not specify all variants.
template 
struct GenericVisitor{};
template 
struct GenericVisitor: public GenericVisitor
{
	using GenericVisitor::operator ();
	explicit GenericVisitor(
		std::function _visitor,
		std::function... _otherVisitors
	):
		GenericVisitor(std::move(_otherVisitors)...),
		m_visitor(std::move(_visitor))
	{}
	void operator()(Visitable& _v) const { m_visitor(_v); }
	std::function m_visitor;
};
template <>
struct GenericVisitor<>: public boost::static_visitor<> {
	void operator()() const {}
};
/// Generic visitor with fallback:
/// boost::apply_visitor(GenericFallbackVisitor(
///     [](Class1& _c) { _c.f(); },
///     [](Class2& _c) { _c.g(); }
/// ), variant);
/// This one DOES have a fallback and will NOT fail at
/// compile-time if you do not specify all variants.
template 
struct GenericFallbackVisitor{};
template 
struct GenericFallbackVisitor: public GenericFallbackVisitor
{
	explicit GenericFallbackVisitor(
		std::function _visitor,
		std::function... _otherVisitors
	):
		GenericFallbackVisitor(std::move(_otherVisitors)...),
		m_visitor(std::move(_visitor))
	{}
	using GenericFallbackVisitor::operator ();
	void operator()(Visitable& _v) const { m_visitor(_v); }
	std::function m_visitor;
};
template <>
struct GenericFallbackVisitor<>: public boost::static_visitor<> {
	template 
	void operator()(T&) const { }
};
/// Generic visitor with fallback that can return a value:
/// boost::apply_visitor(GenericFallbackReturnsVisitor(
///     [](Class1& _c) { return _c.f(); },
///     [](Class2& _c) { return _c.g(); }
/// ), variant);
/// This one DOES have a fallback and will NOT fail at
/// compile-time if you do not specify all variants.
/// The fallback {}-constructs the return value.
template 
struct GenericFallbackReturnsVisitor{};
template 
struct GenericFallbackReturnsVisitor: public GenericFallbackReturnsVisitor
{
	explicit GenericFallbackReturnsVisitor(
		std::function _visitor,
		std::function... _otherVisitors
	):
		GenericFallbackReturnsVisitor(std::move(_otherVisitors)...),
		m_visitor(std::move(_visitor))
	{}
	using GenericFallbackReturnsVisitor::operator ();
	R operator()(Visitable& _v) const { return m_visitor(_v); }
	std::function m_visitor;
};
template 
struct GenericFallbackReturnsVisitor: public boost::static_visitor {
	template 
	R operator()(T&) const { return {}; }
};
}