blob: dfae8e4bc3b1d57fe64cec0978a16bc887a1b2a4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
**/
#ifndef QUICKSTEP_UTILITY_TEMPLATE_UTIL_HPP_
#define QUICKSTEP_UTILITY_TEMPLATE_UTIL_HPP_
#include <cstddef>
#include <tuple>
#include <utility>
namespace quickstep {
/** \addtogroup Utility
* @{
*/
namespace template_util_inner {
/**
* @brief Represents a compile-time sequence of integers.
*
* Sequence is defined here for C++11 compatibility. For C++14 and above,
* std::integer_sequence can be used to achieve the same functionality.
*
* TODO(jianqiao): directly use std::integer_sequence if having C++14 support.
*/
template<std::size_t ...>
struct Sequence {};
/**
* @brief The helper class for creating Sequence. MakeSequence<N>::type is
* equivalent to Sequence<1,2,...,N>.
*
* MakeSequence is defined here for C++11 compatibility. For C++14 and above,
* std::make_index_sequence can be used to achieve the same functionality.
*
* TODO(jianqiao): directly use std::make_index_sequence if having C++14 support.
*/
template<std::size_t N, std::size_t ...S>
struct MakeSequence : MakeSequence<N-1, N-1, S...> {};
template<std::size_t ...S>
struct MakeSequence<0, S...> {
typedef Sequence<S...> type;
};
/**
* @brief Final step of CreateBoolInstantiatedInstance. Now all bool_values are
* ready. Instantiate the template and create (i.e. new) an instance.
*/
template <template <bool ...> class T, class ReturnT,
bool ...bool_values, std::size_t ...i,
typename Tuple>
inline ReturnT* CreateBoolInstantiatedInstanceInner(Tuple &&args,
Sequence<i...> &&indices) {
return new T<bool_values...>(std::get<i>(std::forward<Tuple>(args))...);
}
/**
* @brief Invoke the functor with the compile-time bool values wrapped as
* integral_constant types.
*/
template <typename FunctorT, bool ...bool_values>
inline auto InvokeOnBoolsInner(const FunctorT &functor) {
return functor(std::integral_constant<bool, bool_values>()...);
}
/**
* @brief Recursive dispatching.
*/
template <typename FunctorT, bool ...bool_values, typename ...Bools>
inline auto InvokeOnBoolsInner(const FunctorT &functor,
const bool tparam,
const Bools ...rest_params) {
if (tparam) {
return InvokeOnBoolsInner<FunctorT, bool_values..., true>(
functor, rest_params...);
} else {
return InvokeOnBoolsInner<FunctorT, bool_values..., false>(
functor, rest_params...);
}
}
/**
* @brief Move the functor to the first position in argument list.
*/
template <std::size_t last, std::size_t ...i, typename TupleT>
inline auto InvokeOnBoolsInner(TupleT &&args, Sequence<i...> &&indices) {
return InvokeOnBoolsInner(std::get<last>(std::forward<TupleT>(args)),
std::get<i>(std::forward<TupleT>(args))...);
}
} // namespace template_util_inner
/**
* @brief Edge case of the recursive CreateBoolInstantiatedInstance function
* when all bool variables have been branched and replaced with compile-time
* bool constants.
*/
template <template <bool ...> class T, class ReturnT,
bool ...bool_values,
typename Tuple>
inline ReturnT* CreateBoolInstantiatedInstance(Tuple &&args) {
// Note that the constructor arguments have been forwarded as a tuple (args).
// Here we generate a compile-time index sequence (i.e. typename MakeSequence<n_args>::type())
// for the tuple, so that the tuple can be unpacked as a sequence of constructor
// parameters in CreateBoolInstantiatedInstanceInner.
constexpr std::size_t n_args = std::tuple_size<Tuple>::value;
return template_util_inner::CreateBoolInstantiatedInstanceInner<
T, ReturnT, bool_values...>(
std::forward<Tuple>(args),
typename template_util_inner::MakeSequence<n_args>::type());
}
/**
* @brief A helper function for creating bool branched templates.
*
* The scenario for using this helper function is that, suppose we have a class
* where all template parameters are bools:
* --
* template <bool c1, bool c2, bool c3>
* class SomeClass : public BaseClass {
* // This simple function will be invoked in computationally-intensive loops.
* inline SomeType someSimpleFunction(...) {
* if (c1) {
* doSomeThing1();
* }
* if (c2) {
* doSomeThing2();
* }
* if (c3) {
* doSomeThing3();
* }
* }
* };
* --
* Typically, this bool-paramterized template is for performance consideration.
* That is, we would like to make a copy of code for each configuration of bool
* values, so that there will be no branchings in someSimpleFunction().
*
* The problem is that, to conditionally instantiate the template, given bool
* variables c1, c2, c3, we have to do something like this:
* --
* if (c1) {
* if (c2) {
* if (c3) {
* return new SomeClass<true, true, true>(some_args...);
* } else {
* return new SomeClass<true, true, false>(some_args...);
* }
* } else {
* if (c3) {
* return new SomeClass<true, false, true>(some_args...);
* } else {
* return new SomeClass<true, false, false>(some_args...);
* }
* } else {
* ...
* }
* --
* Then there will be power(2,N) branches if the template has N bool parameters,
* making it tedious to do the instantiating.
*
* Now, this helper function can achieve the branched instantiation in one
* statement as:
* --
* return CreateBoolInstantiatedInstance<SomeClass,BaseClass>(
* std::forward_as_tuple(some_args...), c1, c2, c3);
* --
*/
template <template <bool ...> class T, class ReturnT,
bool ...bool_values, typename ...Bools,
typename Tuple>
inline ReturnT* CreateBoolInstantiatedInstance(Tuple &&args,
const bool tparam,
const Bools ...rest_tparams) {
if (tparam) {
return CreateBoolInstantiatedInstance<T, ReturnT, bool_values..., true>(
std::forward<Tuple>(args), rest_tparams...);
} else {
return CreateBoolInstantiatedInstance<T, ReturnT, bool_values..., false>(
std::forward<Tuple>(args), rest_tparams...);
}
}
/**
* @brief A helper function for bool branched template specialization.
*
* Usage example:
* --
* bool c1 = true, c2 = false;
*
* InvokeOnBools(
* c1, c2,
* [&](auto c1, auto c2) -> SomeBaseClass* {
* using T1 = decltype(c1); // T1 == std::true_type
* using T2 = decltype(c2); // T2 == std::false_type
*
* constexpr bool cv1 = T1::value; // cv1 == true
* constexpr bool cv2 = T2::value; // cv2 == false
*
* SomeFunction<cv1, cv2>(...);
* return new SomeClass<cv1, cv2>(...);
* });
* --
*/
template <typename ...ArgTypes>
inline auto InvokeOnBools(ArgTypes ...args) {
constexpr std::size_t last = sizeof...(args) - 1;
return template_util_inner::InvokeOnBoolsInner<last>(
std::forward_as_tuple(args...),
typename template_util_inner::MakeSequence<last>::type());
}
/** @} */
} // namespace quickstep
#endif // QUICKSTEP_UTILITY_TEMPLATE_UTIL_HPP_