Подтвердить что ты не робот

Как развернуть короткий цикл в С++

Интересно, как получить что-то вроде этого:

  • Запись

    copy(a, b, 2, 3)
    
  • И затем получите

    a[2] = b[2];
    a[3] = b[3];
    a[4] = b[4];
    

Я знаю, что С#defines нельзя использовать рекурсивно для получения этого эффекта. Но я использую С++, поэтому я предполагаю, что мета-программирование шаблона может быть уместным.

Я знаю, что для этого есть библиотека Boost, но мне нужен только этот "простой" трюк, а Boost слишком "грязный".

4b9b3361

Ответ 1

Наиболее простым решением для этого является запись цикла, в котором известны начальное и конечное значения:

for(int i = 2; i <= 4; i++) {
  a[i]=b[i]; 
}

Я думаю, что это лучше, чем любая смесь шаблонов /runtime -call: петля, написанная, полностью понятна оптимизатору компиляторов, и нет уровней вызовов функций, чтобы прорыть, чтобы увидеть, что происходит.

Ответ 2

Мета-программирование С++ рекурсивно.

Подумайте о своей проблеме с точки зрения рекурсии. Внедрение терминальных и нетерминальных случаев.

Ваш терминал может быть как 0, так и один. Пределы пропуска в качестве параметров шаблона. Используйте структуру/класс, потому что они позволяют частичную специализацию и другие опрятные вещи:

template<int from, int to>
struct copy {
    static void apply(source, destination) {
        source[from] = destination[from];
        copy<from+1,to>:: apply(source, destination);
    }
};

// Terminal case
template<int from>
struct copy<from, from> {
    static void apply(source, destination) {
        source[from] = destination[from];
    }
};

Ответ 3

Возможно, вы можете сделать что-то вроде следующего. В зависимости от вашего компилятора и параметров оптимизации, которые вы используете, вы можете получить тот эффект, который вы ищете.

Помните, что для небольших объектов, таких как char, он может быть медленнее, чем std::copy или memcpy, а для более крупных объектов стоимость цикла может быть незначительной по сравнению с копиями, которые происходят в в любом случае.

#include <cstddef>

template<std::size_t base, std::size_t count, class T, class U>
struct copy_helper
{
    static void copy(T dst, U src)
    {
        dst[base] = src[base];
        copy_helper<base + 1, count - 1, T, U>::copy(dst, src);
    }
};

template<std::size_t base, class T, class U>
struct copy_helper<base, 0, T, U>
{
    static void copy(T, U)
    {
    }
};

template<std::size_t base, std::size_t count, class T, class U>
void copy(T dst, U src)
{
    copy_helper<base, count, T, U>::copy(dst, src);
}

template void copy<5, 9, char*, const char*>(char*, const char*);

#include <iostream>
#include <ostream>

int main()
{
    const char test2[] = "     , World\n";
    char test[14] = "Hello";

    copy<5, 9>(test, test2);

    std::cout << test;

    return 0;
}

Ответ 4

Важно понимать, что компилятор очень умный, и что обманывать его для разворачивания циклов с использованием метапрограммирования шаблонов, вероятно, заставит вас вернуться к дальнейшему продвижению.

Чтобы получить нижнюю часть ваших оптимизаций: следите за разборкой. Это, надеюсь, научит вас больше, чем бросать шаблоны в проблему.

И обратите внимание, как сказал Йоханнес: если компилятор увидит, что вы запускаете цикл для фиксированного количества раз (или фиксированного кратного раза, например 4x-переменной), он может создать код, очень близкий к оптимальному.

Ответ 6

Он не использует шаблоны, и это не "полная" разворачивание, но вы можете частично развернуть цикл с чем-то вроде этого:

void copy (SomeType* a, SomeType* b, int start_index, int num_items) {
    int i = start_index;

    while (num_items > 4) {
            a[i+0] = b[i+0];
            a[i+1] = b[i+1];
            a[i+2] = b[i+2];
            a[i+3] = b[i+3];
            i += 4;
            num_items -= 4;
    }
    while (num_items > 0) {
            a[i] = b[i];
            ++i;
            --num_items;
    }
}

Теперь в этом конкретном примере дополнительные вычисления, вероятно, перевешивают преимущества только одновременного разворачивания четырех элементов. Вы должны получать все большее преимущество от возрастающего числа элементов внутри верхнего цикла (во всей функции, заменяйте 4 тем же количеством элементов, которые вы копируете внутри каждой развернутой вручную итерации).

Ответ 7

template <int begin, int end> struct copy_;

template <int end> struct copy_<end, end> {
  template <typename T> static void execute(T& a, T& b)
  {
    a[end] = b[end];
  }
};

template <int begin, int end> struct copy_<begin, end> {
  template <typename T> static void execute(T& a, T& b)
  {
    a[begin] = b[begin];
    copy_<begin+1, end>::execute(a, b);
  }
};

template <int begin, int how_many> struct copy {
  template <typename T> static void execute(T& a, T& b)
  {
    copy_<begin, begin+how_many-1>::execute(a, b);
  }
};

copy<2, 3>::execute(a, b);

Ответ 8

Развертывание цикла с использованием метапрограмм может использоваться для создания constexpr (я не измерял время). У меня есть пример, где его можно использовать для создания функциональной комбинации (n, k) a cosntexpr:

template <size_t N> struct iterate_forward {
    template <class operation> static auto eval(const operation & op)
    {
        iterate_forward<N-1>::eval(op);
        return op(N-1);
    }
};
template <> struct iterate_forward<1>
{
    template <class operation> static auto eval(const operation & op)
    { return op(0); }
};
template <> struct iterate_forward<0>
{
    template <class operation> static auto eval(const operation & op) {}
};
template <size_t N, size_t K> constexpr size_t COMB()
{
    struct ratio
    {
        size_t operator () (size_t i) const { return (res *= (N-i) / (i+1)); }
        mutable size_t res = 1;
    };
    return iterate_forward<K>::eval(ratio());
} 

Ответ 9

#define tl template
#define tn typename
#define st struct
#define cl class
#define us using


    template<tn A> st Typ { using type = A; };
    tl<tn T> using GetT = tn T::type;
    tl<tn F, tn ...As> us apply = tn F::tl apply<As...>;
    tl<tn, tn, tn ...> st LoopElements;
    tl<tn, tn> st Loop;
    tl<tn, tn, tn> st VLoop_i;
    tl<tn Sq, tn MF> us VLoop = VLoop_i<GetT<Sq>, Sq, MF>;

    //
    // TY
    //
    template<tn T> struct Ty {
        template<T ...> struct Pack : Typ<T> {};
        tl<tn ...> st Concat_i; tl<tn ...P> us Concat = GetT<Concat_i<P...>>;
        tl<T, int64_t> st Seq_i; tl<T f, T t> us Seq = GetT<Seq_i<f, ((int64_t)(t - f))>>; tl<int64_t, tn> st add;

        template<tl<T ...> tn C, T ...vs, tl<T ...> tn D, T ...ws, tn ...R> st Concat_i<C<vs...>, D<ws...>, R...> : Typ<Concat<C<vs..., ws...>, R...> >{};
        template<tl<T ...> tn C, T ...vs> st Concat_i<C<vs...>> : Typ<C<vs...> >{};

        template<int64_t x, T ...vs> struct add<x, Pack<vs...>> : Typ<Pack<((T)(vs + x))...>> {};
        template<T f, int64_t c> class Seq_i {
            using A = tn Seq_i<f, c/2>::type;
            using B = tn add<c/2, A>::type;
            using C = tn Seq_i<f + c / 2 * 2, c & 1>::type;
        public:
            using type = Concat<A, B, C>;
        };
        tl<T f> st Seq_i<f, 0> : Typ<Pack<>> {};        
        tl<T f> st Seq_i<f, 1> : Typ<Pack<f>> {};
        tl<T f> st Seq_i<f, -1> : Typ<Pack<f>> {};
    };

    //
    // LOOP
    template<size_t i, tn F, tn T> struct LoopElement { LoopElement() { apply<F, T>(); }; };
    template<size_t ...is, tn F, tn ...P> struct LoopElements<Ty<size_t>::Pack<is...>, F, P...> : LoopElement<is, F, P>... {};
    template<tn F, tl<tn ...> cl C, tn ...P> struct Loop<F, C<P...>> : LoopElements<Ty<size_t>::Seq<0, sizeof...(P)>, F, P...> {};

    template<tn T, tl<T ...> cl ST, T ...vs, tn MF> struct VLoop_i<T, ST<vs...>, MF> : LoopElements<Ty<size_t>::Seq<0, sizeof...(vs)>, MF, Val<T, vs>...> {};