Home > Blockchain >  How to remove an element from a pack in C ?
How to remove an element from a pack in C ?

Time:06-12

I'm trying to remove an element from a C pack. It's a hard to explain with words so I will just show you what I want in code.

// lets say I have the following function
template<typename... Args>
void foo1(Args... arguments)
{
    // does something with the arguments
}

// and another similar function which needs to call foo1 but with a modified pack
template<typename... Args>
void foo2(Args... arguments)
{
   // foo2 figures out what arguments should be removed from the "arguments" pack
   // and here comes the hard part, after I know the indices of what elements to remove, how do I remove them from the pack?
   // then foo2 calls foo1 with the new pack (modified argument list)
   foo1(new_arguments...);
}

I want a pure C solution without including any files because it should work for kernel mode and you can't include any standard C library in kernel mode.

Any ideas how to do it?

EDIT: The indices are constexpr integer values so I can use them in templates or anything like that.

CodePudding user response:

I found a solution, it's not exactly what I wanted because it uses std::tuple but it's good enough for now. Here is the code:

#include <tuple>

struct remove_element_from_pack
{
private:
    template<typename Ty, typename... Args>
    __forceinline constexpr static Ty get_first_val(Ty val, Args... other) { return val; }

    template<typename Ty, typename... Args>
    __forceinline constexpr static auto remove_first_from_tuple(Ty first, Args... rest)
    {
        return std::tuple<Args...>(rest...);
    }

    template<typename return_ty, typename... Args>
    __forceinline constexpr static return_ty call_func_internal(const void* function, Args... arguments)
    {
        return ((return_ty(__fastcall*)(Args...))function)(arguments...);
    }

public:
    template<
        typename return_ty,
        int current_index,
        int remove_current,
        int... remove,
        typename current_ty,
        typename... All,
        typename... Processed>
        __forceinline static return_ty call_func(const void* function, std::tuple<current_ty, All...> all, std::tuple<Processed...> processed)
    {
        auto current = std::apply([](auto&&... args)->auto { return get_first_val(args...); }, all);

        // if there are no more elements
        if constexpr (!sizeof...(All))
        {
            // if we have to remove the current element
            if constexpr (remove_current != -1 && is_in_pack<int, current_index, remove_current, remove...>::value)
            {
                return std::apply(
                    [](auto &&... args)->return_ty { return call_func_internal<return_ty>(args...); },
                    std::tuple_cat(std::make_tuple(function), processed)
                );
            }
            else
            {
                return std::apply(
                    [](auto &&... args)->return_ty { return call_func_internal<return_ty>(args...); },
                    std::tuple_cat(std::make_tuple(function), std::tuple_cat(processed, std::make_tuple(current)))
                );
            }
        }
        else
        {
            auto new_all = std::apply([](auto&&... args)->auto { return remove_first_from_tuple(args...); }, all);

            // if we have to remove the current element
            if constexpr (remove_current != -1 && is_in_pack<int, current_index, remove_current, remove...>::value)
            {
                // if there are any elements left to remove
                if constexpr (sizeof...(remove) > 0)
                {
                    return std::apply(
                        [](auto &&... args)->return_ty { return call_func<return_ty, current_index   1, remove...>(args...); },
                        std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(processed))
                    );
                }
                else
                {
                    return std::apply(
                        [](auto &&... args)->return_ty { return call_func<return_ty, current_index   1, -1>(args...); },
                        std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(processed))
                    );
                }
            }
            else
            {
                auto new_processed = std::tuple_cat(processed, std::make_tuple(current));

                return std::apply(
                    [](auto &&... args)->return_ty { return call_func<return_ty, current_index   1, remove_current, remove...>(args...); },
                    std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(new_processed))
                );
            }
        }
    }
};

Then you can call the function for example like this:

// target function
int __fastcall add2(double a, double b)
{
    return a   b;
}

// call
remove_element_from_pack::call_func<int, 0, 0, 3>(
        add2, std::tuple<double, double, double, double>(20.0, 30.0, 40.0, 29.0), std::tuple()
    );

In this example the 0 and 3rd element will be removed from the pack (first tuple in the function call) which means 20.0 and 29.0 will be excluded and add2 will be called with 30.0 and 40.0.

EDIT: I forgot to post this part of the code:

template<typename Ty, Ty element, Ty first, Ty... rest_of_pack>
struct is_in_pack
{
private:
    __forceinline static constexpr bool get_value_internal()
    {
        if constexpr (first == element)
            return true;
        else if constexpr (!sizeof...(rest_of_pack))
            return false;
        else
            return is_in_pack<Ty, element, rest_of_pack...>::value;
    }

public:
    static constexpr const bool value = get_value_internal();
};

CodePudding user response:

Here's a C 20 solution without using any standard library headers.

It defines a type trait take which collects a list of N types from the front of a pack, and then uses the list to define a lambda that partitions the arguments of foo2 and drops the Nth index at each recursion until no drop indices are left before delegating to foo1.

namespace detail {

template <class...>
struct list {};

template <int, class, class = list<>>
struct take;

template <class Drop, class Take>
struct take<0, Drop, Take> {
  using type = Take;
};

template <int N, class T, class... Drop, class... Take>
  requires(N > 0)
struct take<N, list<T, Drop...>, list<Take...>>
    : take<N - 1, list<Drop...>, list<Take..., T>> {};

}  // namespace detail

template <class... Args>
void foo2(Args... new_arguments) {
  foo1(new_arguments...);
}

template <int Index, int... Indices, class... Args>
void foo2(Args... arguments) {
  [&]<class... Take>(detail::list<Take...>) {
    [](Take... take, auto, auto... rest) {
      foo2<(Indices - 1)...>(take..., rest...);
    }(arguments...);
  }(typename detail::take<Index, detail::list<Args...>>::type{});
}
  • Related