0%

Simulation for compile-time loop

問題說來很簡單,結果我花了好久才搞懂,字字血淚
我想要的只有這樣的效果

1
2
3
4
5
6
7
8
template <std::size_t i>
void inner() {}

tempplate <std::size_t N>
void outer() {
for (std::size_t i = 0; i < N; i++)
inner<i>();
}

結果這樣是無法編譯的,只好找其他方法繞過

Solution 1: Template specialization

一樣是從別人的Code上學來的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <std::size_t i>
void inner() {}

template <std::size_t N>
struct helper {
    template <typename Indices = std::make_index_sequence<N>>
    struct _helper;

    template <std::size_t... Idx>
    struct _helper<std::index_sequence<Idx...>> {
        static void unroll() {
            (inner<Idx>(), ...);
        }
    };
};

template <std::size_t N>
void outer()
{
        helper<N>::template _helper<>::unroll();
}

在這邊

  • _helper會有兩個Template definition,而由於_helper<std::index_sequence<Idx...>>的契合度比primary template好,所以會選擇這個definition
  • 然後就用fold expression展開了
  • 不過實在是太醜了,思考有無其他的方案

    Fail attempt:

    在求助於AI之下,嘗試了其他方案

    std::apply and std::make_integer_sequence

    這個是Claude提供的
    1
    2
    3
    4
    5
    template <size_t i> void inner() {}
    template <size_t N>
    void outer() {
    std::apply([](auto... indices) { (inner<indices>(), ...); }, std::make_index_sequence<N>{});
    }
    結果是不能用,std::apply需要接受一個tuple like的參數,很可惜這不是
    於是先擱置在一旁,不過這個方案接近我最後的方案,還是感激一下AI

    Custom apply function

    這個是GPT4-o提供的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template <typename F, size_t... Indices> 
    void apply_impl(F&& f, std::index_sequence<Indices...>) {
    (f(std::integral_constant<size_t, Indices>{}), ...);
    }
    struct outer {
    template <std::size_t N>
    void test() {
    apply_impl([](auto index) { inner<index>(); }, std::make_index_sequence<N>{});
    }
    };
    將fold expression隱藏於實作中,雖然不錯,不過這方法也是有其缺點
    無法表示這樣的Pseudo code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <std::size_t i>
    bool inner() {
    if (i == 1) return false;
    else return true;
    }

    tempplate <std::size_t N>
    bool outer() {
    for (std::size_t i = 0; i < N; i++)
    if (!inner<i>()) return false;
    return true;
    }
    萬能的CharGPT也給了一個思路,拓展apply_impl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename F, size_t First, size_t... Rest>
    bool apply_impl(F&& f, std::index_sequence<First, Rest...>) {
    if (!f(std::integral_constant<size_t, First>{})) {
    return false;
    }
    return apply_impl(f, std::index_sequence<Rest...>{});
    }

    template <typename F>
    bool apply_impl(F&&, std::index_sequence<>) {
    return true;
    }
    看起來可行,不過也是有自己的問題
  • 當你邏輯改變,例如將all_true或城anyone_true的時候,上面的apply_impl就要改了
  • 當Callback function有無回傳值的情況,就要考慮很多地方,整個邏輯也會變得支離破碎,不好維護
    因此,想出了自己的方案

    Solution2: make_index_sequence_tuple

    查了一下資料std::apply接受的是tuple like的類型,那麼就自己做出一個tuple likeindex_sequqence就好了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template <std::size_t... Idx>
    constexpr auto _make_index_sequence_tuple(std::index_sequence<Idx...>)
    {
    return std::make_tuple(std::integral_constant<std::size_t, Idx>()...);
    }

    template <std::size_t N>
    constexpr auto make_index_sequence_tuple()
    {
    return _make_index_sequence_tuple(std::make_index_sequence<N>{});
    }
    struct outer {
    template <std::size_t N>
    void test() {
    std::apply([](auto... indices) {
    (inner<indices>(), ...); },
    make_index_sequence_tuple<N>()
    );
    }
    };
    這樣就沒啥問題了

    Conclusion

    AI生成的Source Code還是不能照單全收,不過提供個思路總是好的
    不過更好的方法應該是template for,不過不知會部會進標準