adapting a non-constexpr integral value to a non-type template parameter, and code bloat

前端 未结 4 1418
梦毁少年i
梦毁少年i 2021-01-13 03:03

Consider a function object F taking a constexpr size_t argument I

struct F
{
    template 
    constex         


        
相关标签:
4条回答
  • 2021-01-13 03:17

    I'll take the obvious position here and ask if "I want to emphasize that it is constexpr by using it as a template argument" is worth this cost and if:

    struct F
    {
        constexpr size_t operator()(size_t i) const { return i; }
        template <size_t I>
        constexpr size_t operator()(size <I>) const { return (*this)(I); }
    };
    

    would not be a much simpler solution.

    0 讨论(0)
  • 2021-01-13 03:25

    This is not exactly an answer and my question still stands, yet I have found a workaround that gives an impressive boost in compilation. It is a minor tweak of the solution given in the question, where parameter R is moved from operator() outside to structure idx, and the terminating condition now includes both R and N:

    template <
        typename F, size_t L,
        size_t R = 1, size_t N = 0, bool = (R < 2 * L && N < L)
    >
    struct idx //...
    

    The entire code is given in this new live example.

    This approach apparently cuts down a huge number of unnecessary specialization combinations for R. Compile times and executable sizes drop dramatically. For instance, I have been able to compile in 10.7/18.7 seconds with Clang/GCC for L = 1<<12 (4096), yielding an executable of 220/239 KB. In this case, nm -C shows 546/250 versions of operator().

    0 讨论(0)
  • 2021-01-13 03:32

    I call this technique the magic switch.

    The most efficient way I know of to do this is to build your own jump table.

    // first, index list boilerplate.  Does log-depth creation as well
    // needed for >1000 magic switches:
    template<unsigned...Is> struct indexes {typedef indexes<Is...> type;};
    template<class lhs, class rhs> struct concat_indexes;
    template<unsigned...Is, unsigned...Ys> struct concat_indexes<indexes<Is...>, indexes<Ys...>>{
        typedef indexes<Is...,Ys...> type;
    };
    template<class lhs, class rhs>
    using ConcatIndexes = typename concat_indexes<lhs, rhs>::type;
    
    template<unsigned min, unsigned count> struct make_indexes:
        ConcatIndexes<
            typename make_indexes<min, count/2>::type,
            typename make_indexes<min+count/2, (count+1)/2>::type
        >
    {};
    template<unsigned min> struct make_indexes<min, 0>:
        indexes<>
    {};
    template<unsigned min> struct make_indexes<min, 1>:
        indexes<min>
    {};
    template<unsigned max, unsigned min=0>
    using MakeIndexes = typename make_indexes<min, max-min>::type;
    
    // This class exists simply because [](blah){code}... `...` expansion
    // support is lacking in many compilers:
    template< typename L, typename R, unsigned I >
    struct helper_helper {
        static R func( L&& l ) { return std::forward<L>(l)(size<I>()); }
    };
    // the workhorse.  Creates an "manual" jump table, then invokes it:
    template<typename L, unsigned... Is>
    auto
    dispatch_helper(indexes<Is...>, L&& l, unsigned i)
    -> decltype( std::declval<L>()(size<0>()) )
    {
      // R is return type:
      typedef decltype( std::declval<L>()(size<0>()) ) R;
      // F is the function pointer type for our jump table:
      typedef R(*F)(L&&);
      // the jump table:
      static const F table[] = {
        helper_helper<L, R, Is>::func...
      };
      // invoke at the jump spot:
      return table[i]( std::forward<L>(l) );
    };
    // create an index pack for each index, and invoke the helper to
    // create the jump table:
    template<unsigned Max, typename L>
    auto
    dispatch(L&& l, unsigned i)
    -> decltype( std::declval<L>()(size<0>()) )
    {
      return dispatch_helper( MakeIndexes<Max>(), std::forward<L>(l), i );
    };
    

    which requires some static setup, but is pretty fast when run.

    An assert that i is in bounds may also be useful.

    live example

    0 讨论(0)
  • 2021-01-13 03:38

    If your solution have cap on maximum possible value (say 256) you can use macro magic and switch statement to simplify it:

    #define POS(i) case (i): return F<(i)>(); break;
    #define POS_4(i) POS(i + 0) POS(i + 1) POS(i + 2) POS(i + 3)
    #define POS_16(i) POS_4(i + 0) POS_4(i + 4) POS_4(i + 8) POS_4(i + 12)
    
    int func(int i)
    {
        switch(i)
        {
            POS_16(0)
        }
    }
    

    Another possible solution is (with C++11) use variadic templates:

    template<int I>
    struct FF
    {
        static int f() { return I; }
    };
    
    
    template<typename... I>
    int func(int i)
    {
        constexpr int (*Func[])() = { I::f... };
        return Func[i]();
    }
    
    int main(int argc, char** argv)
    {
        func<FF<0>,FF<1>>(1);
    }
    
    0 讨论(0)
提交回复
热议问题