1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 00:03:03 +04:00

Merge pull request #20039 from sivanov-work:gapi_empty_input

G-API: Implement variant visit()

* Add variant visitor, use visitor for check compile args

* Fix GAPI UT: variant *compiler

* Aling apply_visior with std, fix indentations

* Fix compilation (included compiler_hints.hpp)

* Fix compilation (due gapi standalone)

* Fix compilation2 (Docs)

* Add Lambdas overload, Refactor visit()

* Add ReturnType auto deduction

* Fix comilation

* Fix compilation

* Fix warnings

* Try to fix MSVC14

* Fix docs

* Try fix Win compile

* Fix Docs again

* Revert GAPI empty input fix

* Apply comment for `tuple_element`

* Add std::decay for std::base_of to work arounf armv7 problem

* Apply review comments

* Apply review comments: added comment & removed unused args

* Fix docs compilation
This commit is contained in:
Sergey Ivanov
2021-07-07 15:33:40 +03:00
committed by GitHub
parent ed2a698392
commit c0f63eb21f
6 changed files with 512 additions and 20 deletions
@@ -43,19 +43,6 @@ namespace detail
GOPAQUE, // a cv::GOpaqueU (note - exactly GOpaqueU, not GOpaque<T>!)
};
template<typename T>
constexpr const char* meta_to_string() noexcept;
template<>
constexpr const char* meta_to_string<cv::GMatDesc>() noexcept { return "GMatDesc"; }
template<>
constexpr const char* meta_to_string<cv::GScalarDesc>() noexcept { return "GScalarDesc"; }
template<>
constexpr const char* meta_to_string<cv::GArrayDesc>() noexcept { return "GArrayDesc"; }
template<>
constexpr const char* meta_to_string<cv::GOpaqueDesc>() noexcept { return "GOpaqueDesc"; }
template<>
constexpr const char* meta_to_string<cv::GFrameDesc>() noexcept { return "GFrameDesc";}
// Describe G-API types (G-types) with traits. Mostly used by
// cv::GArg to store meta information about types passed into
// operation arguments. Please note that cv::GComputation is
@@ -35,7 +35,6 @@ namespace detail
template<> struct ProtoToMeta<cv::GScalar> { using type = cv::GScalarDesc; };
template<typename U> struct ProtoToMeta<cv::GArray<U> > { using type = cv::GArrayDesc; };
template<typename U> struct ProtoToMeta<cv::GOpaque<U> > { using type = cv::GOpaqueDesc; };
template<> struct ProtoToMeta<cv::GFrame> { using type = cv::GFrameDesc; };
template<typename T> using ProtoToMetaT = typename ProtoToMeta<T>::type;
//workaround for MSVC 19.0 bug
@@ -117,6 +117,43 @@ namespace detail
static type get(std::tuple<Objs...>&& objs) { return std::forward<std::tuple<Objs...>>(objs); }
};
} // namespace detail
namespace util
{
template<typename ...L>
struct overload_lamba_set;
template<typename L1>
struct overload_lamba_set<L1> : public L1
{
overload_lamba_set(L1&& lambda) : L1(std::move(lambda)) {}
overload_lamba_set(const L1& lambda) : L1(lambda) {}
using L1::operator();
};
template<typename L1, typename ...L>
struct overload_lamba_set<L1, L...> : public L1, public overload_lamba_set<L...>
{
using base_type = overload_lamba_set<L...>;
overload_lamba_set(L1 &&lambda1, L&& ...lambdas):
L1(std::move(lambda1)),
base_type(std::forward<L>(lambdas)...) {}
overload_lamba_set(const L1 &lambda1, L&& ...lambdas):
L1(lambda1),
base_type(std::forward<L>(lambdas)...) {}
using L1::operator();
using base_type::operator();
};
template<typename... L>
overload_lamba_set<L...> overload_lambdas(L&& ...lambdas)
{
return overload_lamba_set<L...>(std::forward<L>(lambdas)...);
}
}
} // namespace cv
// \endcond
@@ -11,6 +11,7 @@
#include <array>
#include <type_traits>
#include <opencv2/gapi/util/compiler_hints.hpp>
#include <opencv2/gapi/util/throw.hpp>
#include <opencv2/gapi/util/util.hpp> // max_of_t
#include <opencv2/gapi/util/type_traits.hpp>
@@ -44,6 +45,12 @@ namespace util
static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
};
template<std::size_t Index, class... Types >
struct type_list_element
{
using type = typename std::tuple_element<Index, std::tuple<Types...> >::type;
};
class bad_variant_access: public std::exception
{
public:
@@ -233,9 +240,87 @@ namespace util
template<typename T, typename... Types>
const T& get(const util::variant<Types...> &v);
template<std::size_t Index, typename... Types>
typename util::type_list_element<Index, Types...>::type& get(util::variant<Types...> &v);
template<std::size_t Index, typename... Types>
const typename util::type_list_element<Index, Types...>::type& get(const util::variant<Types...> &v);
template<typename T, typename... Types>
bool holds_alternative(const util::variant<Types...> &v) noexcept;
// Visitor
namespace detail
{
struct visitor_interface {};
// Class `visitor_return_type_deduction_helper`
// introduces solution for deduction `return_type` in `visit` function in common way
// for both Lambda and class Visitor and keep one interface invocation point: `visit` only
// his helper class is required to unify return_type deduction mechanism because
// for Lambda it is possible to take type of `decltype(visitor(get<0>(var)))`
// but for class Visitor there is no operator() in base case,
// because it provides `operator() (std::size_t index, ...)`
// So `visitor_return_type_deduction_helper` expose `operator()`
// uses only for class Visitor only for deduction `return type` in visit()
template<typename R>
struct visitor_return_type_deduction_helper
{
using return_type = R;
// to be used in Lambda return type deduction context only
template<typename T>
return_type operator() (T&&);
};
}
// Special purpose `static_visitor` can receive additional arguments
template<typename R, typename Impl>
struct static_visitor : public detail::visitor_interface,
public detail::visitor_return_type_deduction_helper<R> {
// assign responsibility for return type deduction to helper class
using return_type = typename detail::visitor_return_type_deduction_helper<R>::return_type;
using detail::visitor_return_type_deduction_helper<R>::operator();
friend Impl;
template<typename VariantValue, typename ...Args>
return_type operator() (std::size_t index, VariantValue&& value, Args&& ...args)
{
suppress_unused_warning(index);
return static_cast<Impl*>(this)-> visit(
std::forward<VariantValue>(value),
std::forward<Args>(args)...);
}
};
// Special purpose `static_indexed_visitor` can receive additional arguments
// And make forwarding current variant index as runtime function argument to its `Impl`
template<typename R, typename Impl>
struct static_indexed_visitor : public detail::visitor_interface,
public detail::visitor_return_type_deduction_helper<R> {
// assign responsibility for return type deduction to helper class
using return_type = typename detail::visitor_return_type_deduction_helper<R>::return_type;
using detail::visitor_return_type_deduction_helper<R>::operator();
friend Impl;
template<typename VariantValue, typename ...Args>
return_type operator() (std::size_t Index, VariantValue&& value, Args&& ...args)
{
return static_cast<Impl*>(this)-> visit(Index,
std::forward<VariantValue>(value),
std::forward<Args>(args)...);
}
};
template <class T>
struct variant_size;
template <class... Types>
struct variant_size<util::variant<Types...>>
: std::integral_constant<std::size_t, sizeof...(Types)> { };
// FIXME: T&&, const TT&& versions.
// Implementation //////////////////////////////////////////////////////////
@@ -402,6 +487,22 @@ namespace util
throw_error(bad_variant_access());
}
template<std::size_t Index, typename... Types>
typename util::type_list_element<Index, Types...>::type& get(util::variant<Types...> &v)
{
using ReturnType = typename util::type_list_element<Index, Types...>::type;
return const_cast<ReturnType&>(get<Index, Types...>(static_cast<const util::variant<Types...> &>(v)));
}
template<std::size_t Index, typename... Types>
const typename util::type_list_element<Index, Types...>::type& get(const util::variant<Types...> &v)
{
static_assert(Index < sizeof...(Types),
"`Index` it out of bound of `util::variant` type list");
using ReturnType = typename util::type_list_element<Index, Types...>::type;
return get<ReturnType>(v);
}
template<typename T, typename... Types>
bool holds_alternative(const util::variant<Types...> &v) noexcept
{
@@ -428,7 +529,130 @@ namespace util
{
return !(lhs == rhs);
}
} // namespace cv
namespace detail
{
// terminate recursion implementation for `non-void` ReturnType
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, typename... VisitorArgs>
ReturnType apply_visitor_impl(Visitor&&, Variant&,
std::true_type, std::false_type,
VisitorArgs&& ...)
{
return {};
}
// terminate recursion implementation for `void` ReturnType
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, typename... VisitorArgs>
void apply_visitor_impl(Visitor&&, Variant&,
std::true_type, std::true_type,
VisitorArgs&& ...)
{
}
// Intermediate resursion processor for Lambda Visitors
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, bool no_return_value, typename... VisitorArgs>
typename std::enable_if<!std::is_base_of<visitor_interface, typename std::decay<Visitor>::type>::value, ReturnType>::type
apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed,
std::integral_constant<bool, no_return_value> should_no_return,
VisitorArgs&& ...args)
{
static_assert(std::is_same<ReturnType, decltype(visitor(get<CurIndex>(v)))>::value,
"Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`"
" must return the same type");
suppress_unused_warning(not_processed);
if (v.index() == CurIndex)
{
return visitor.operator()(get<CurIndex>(v), std::forward<VisitorArgs>(args)... );
}
using is_variant_processed_t = std::integral_constant<bool, CurIndex + 1 >= ElemCount>;
return apply_visitor_impl<ReturnType, CurIndex +1, ElemCount>(
std::forward<Visitor>(visitor),
std::forward<Variant>(v),
is_variant_processed_t{},
should_no_return,
std::forward<VisitorArgs>(args)...);
}
//Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator()
template<std::size_t CurIndex, typename ReturnType, typename Visitor, class Value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<static_visitor<ReturnType, typename std::decay<Visitor>::type>,
typename std::decay<Visitor>::type>::value, ReturnType>::type
invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args)
{
return static_cast<static_visitor<ReturnType, typename std::decay<Visitor>::type>&>(visitor).operator() (CurIndex, std::forward<Value>(v), std::forward<VisitorArgs>(args)... );
}
//Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator()
template<std::size_t CurIndex, typename ReturnType, typename Visitor, class Value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<static_indexed_visitor<ReturnType, typename std::decay<Visitor>::type>,
typename std::decay<Visitor>::type>::value, ReturnType>::type
invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args)
{
return static_cast<static_indexed_visitor<ReturnType, typename std::decay<Visitor>::type>&>(visitor).operator() (CurIndex, std::forward<Value>(v), std::forward<VisitorArgs>(args)... );
}
// Intermediate recursion processor for special case `visitor_interface` derived Visitors
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, bool no_return_value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<visitor_interface, typename std::decay<Visitor>::type>::value, ReturnType>::type
apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed,
std::integral_constant<bool, no_return_value> should_no_return,
VisitorArgs&& ...args)
{
static_assert(std::is_same<ReturnType, decltype(visitor(get<CurIndex>(v)))>::value,
"Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`"
" must return the same type");
suppress_unused_warning(not_processed);
if (v.index() == CurIndex)
{
return invoke_class_visitor<CurIndex, ReturnType>(visitor, get<CurIndex>(v), std::forward<VisitorArgs>(args)... );
}
using is_variant_processed_t = std::integral_constant<bool, CurIndex + 1 >= ElemCount>;
return apply_visitor_impl<ReturnType, CurIndex +1, ElemCount>(
std::forward<Visitor>(visitor),
std::forward<Variant>(v),
is_variant_processed_t{},
should_no_return,
std::forward<VisitorArgs>(args)...);
}
} // namespace detail
template<typename Visitor, typename Variant, typename... VisitorArg>
auto visit(Visitor &visitor, const Variant& var, VisitorArg &&...args) -> decltype(visitor(get<0>(var)))
{
constexpr std::size_t varsize = util::variant_size<Variant>::value;
static_assert(varsize != 0, "utils::variant must contains one type at least ");
using is_variant_processed_t = std::false_type;
using ReturnType = decltype(visitor(get<0>(var)));
using return_t = std::is_same<ReturnType, void>;
return detail::apply_visitor_impl<ReturnType, 0, varsize, Visitor>(
std::forward<Visitor>(visitor),
var, is_variant_processed_t{},
return_t{},
std::forward<VisitorArg>(args)...);
}
template<typename Visitor, typename Variant>
auto visit(Visitor&& visitor, const Variant& var) -> decltype(visitor(get<0>(var)))
{
constexpr std::size_t varsize = util::variant_size<Variant>::value;
static_assert(varsize != 0, "utils::variant must contains one type at least ");
using is_variant_processed_t = std::false_type;
using ReturnType = decltype(visitor(get<0>(var)));
using return_t = std::is_same<ReturnType, void>;
return detail::apply_visitor_impl<ReturnType, 0, varsize, Visitor>(
std::forward<Visitor>(visitor),
var, is_variant_processed_t{},
return_t{});
}
} // namespace util
} // namespace cv
#endif // OPENCV_GAPI_UTIL_VARIANT_HPP