/*!
* \file invoke.h
* \brief Template meta-programming utilities
*
* Copyright (C) 2018 Christos Choutouridis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#ifndef __utl_meta_invoke_h__
#define __utl_meta_invoke_h__
#include
#include
#include
#include
#include
/*!
* \ingroup meta
* \defgroup invoke
*
*/
//! @{
namespace utl {
namespace meta{
/*!
*
* \name invoke
* All Metafunction classes shall contain apply, which is Metafunction
*
* Metafunction:
* A metafunction is a class or a class template that represents a function invocable at compile-time.
* An non-nullary metafunction is invoked by instantiating the class template with particular template
* parameters (metafunction arguments). The result of the metafunction application is accessible
* through the instantiation's nested type typedef.
* All metafunction's arguments must be types (i.e. only type template parameters are allowed).
* A metafunction can have a variable number of parameters.
* A nullary metafunction is represented as a (template) class with a nested type typename member.
*
* Metafunction Class:
* A metafunction class is a certain form of metafunction representation that enables higher-order
* metaprogramming. More precisely, it's a class with a publicly-accessible nested Metafunction called
* apply. Correspondingly, a metafunction class invocation is defined as invocation of its nested apply
* metafunction.
*
* Concept here is `Invokable` (contains apply metafunction)
*/
//! @{
/*!
* *invocable* identity, identity_t.
*/
//! @{
template
struct identity {
#if defined (UTL_WORKAROUND_CWG_1558)
// decltype via use_() using Ts... to set the apply type
template
using _dummy = void_t;
using apply = _Tp; //!< identity is invokable, must also have apply
#else
template
using apply = _Tp; //!< identity is invokable, must also have apply
#endif
using type = _Tp; //!< identity
};
//! identity type alias
template
using identity_t = type_>;
//! @}
//! Is evaluable trait
//! @{
namespace detail {
// we check for template \p F to be a metafunction with parameters \p T
template class F, typename... T>
struct is_evaluable_ {
template class G, typename = G>
static true_ check (int);
template class>
static false_ check (...);
using type = decltype(check(0));
};
// we check for template \p F with integral constant parameters \p Is of type \p T
// template class F, T... Is>
// struct is_evaluable_i_ {
// template class G, class = G>
// static true_ check (int);
// template class>
// static false_ check (...);
//
// using type = decltype(check(0));
// };
}
template class F, typename... T>
using is_evaluable = type_<
detail::is_evaluable_
>;
// template class F, T... Is>
// using is_evaluable_i = type_>>;
//! @}
//! defer
//! @{
namespace detail {
//! @{
template class F, typename... Ts>
struct defer_ {
using type = F;
};
// template class F, T... Is>
// struct defer_i_ {
// using type = F;
// };
//!
//! We use struct instead of:
//! template class F, typename... Ts>
//! using defer_ = F;
//!
//! The use of struct here is due to Core issue 1430 [1] and is used
//! as suggested by Roy Crihfield in [2].
//! In short, this is due to language's inability to expand Ts... into
//! a fixed parameter list of an alias template.
//!
//! [1]: https://wg21.link/cwg1430
//! [2]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59498
//! @}
}
//! defer alias template for F
template class F, class... Ts>
using defer = if_<
is_evaluable,
detail::defer_,
nil_
>;
//! defer_i alias template for F
// template class F, T... Is>
// using defer_i = if_ <
// is_evaluable_i>,
// detail::defer_i_,
// nil_
// >;
//! @}
/*!
* quote is a higher-order primitive that wraps an n-ary Metafunction
* to create a corresponding Metafunction Class (Invocable).
*/
template class F>
struct quote {
template
using apply = type_>; //!< defer here to avoid DR1430
};
//! Wrap a template \p F taking literals of type \p T into an Invokable
// template class F>
// struct quote_i {
// // requires meta::Integral
// template
// using apply = type_<
// defer_i //!< defer here to avoid DR1430
// >;
// };
/*!
* Invoke the nested apply metafunction from \c Fn
* with the arguments \c Args.
* requires Invocable(Fn)
*/
template
using invoke = typename Fn::template apply;
//! compose
//! @{
namespace detail {
template struct compose_ { };
// recursive call to all invokes
template
struct compose_ {
template
using apply = invoke<
Fn0,
invoke, Args...>
>;
};
// Termination specialization, finally pass the arguments
template
struct compose_ {
template
using apply = invoke;
};
}
/*!
* Create an invokable from other invokables by composition
* ex:
* compose will result to something like F0>>>
*/
template
using compose = detail::compose_;
//! @}
//! Applies the Invocable \p Fn by binding the arguments \p Ts
//! to the \e front of \p Fn.
template
struct bind_front {
template
using apply = invoke;
};
//! Applies the Invocable \p Fn by binding the arguments \p Ts
//! to the \e back of \p Fn.
template
struct bind_back {
template
using apply = invoke;
};
//! @}
}}
//! @}
#endif /* __utl_meta_utility_h__ */