Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract a selected set of arguments of a variadic function and use them to call another function

Tags:

c++

c++11

I have a variadic function zoo which takes N arguments, where N is known at compile time (it is a template parameter of the class containing the function).

template <int N>
struct B
{
    template <typename... Args>
    static void zoo(Args... args)
    {
        static_assert(size of...(args) == N, "");
       // do something
    }
};

I have another variadic function foo which takes M arguments, where M>N and is known at compile time (it is a template parameter of the class containing the function). I have a static index_array containing the indices of the arguments of foo I want to pass to zoo.

From the body of foo I want to call zoo passing a selected subset of the arguments of foo.

What is the best way to do this? Ideally achieving perfect inlining, i.e. so that everything is compiled into just one instruction with no function pointers indirections?

template<int...I>
struct indices
{
    static constexpr int N = sizeof...(I);
};

template <int M, typename...X>
struct A
{
    // here I am simplifying, in reality IS will be built at compile time based on X
    typedef indices<0,2,3> IS;

    template <typename... Args>
    static void foo(Args... args)
    {
        static_assert(size of...(args) == M, "");
       // do some magic to achieve the function call described in pseudo-code
       // B<IS::N>::zoo(args(IS(0),IS(1),IS(2)))
       // ideally this should be perfectly inlined to just have the call above
    }
};

Please note the code above is a simplification of my problem, designed for the purpose of illustrating the question.

EDIT: As asked below, I describe the use case: I am playing with a template based library to drive micro-controller pins. A micro controller has several ports (accessible as bytes in memory) and each port has up to 8 pins (bits). Class A is a bundle of pins via the template argument X, where every pin is defined as Pin. Class B manipulates all pins on the same port. A::foo is a function to modify some of the pins, with arguments in the same order as the order with which the pins are specified in the X template argument pack. foo needs to group the arguments by ports and dispatch to the B classes which representing individual ports, where all arguments are fused and written to the controller in a single instruction.

like image 911
Fabio Avatar asked Jan 17 '16 07:01

Fabio


People also ask

How do you access variadic arguments?

To access variadic arguments, we must include the <stdarg. h> header.

What is special about Variadic functions Python?

Variadic parameters (Variable Length argument) are Python's solution to that problem. A Variadic Parameter accepts arbitrary arguments and collects them into a data structure without raising an error for unmatched parameters numbers.

What is variadic function in postgresql?

You can refer VARIADIC FUNCTIONS IN POSTGRESQL for details. See the wiki about Variadic functions: In computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.

What is variadic function in JavaScript?

A variadic function is designed to accept a variable number of arguments. 1 In JavaScript, you can make a variadic function by gathering parameters. For example: const abccc = (a, b, ... c) => { console.


2 Answers

You can create a helper to extract the nth_arg like this:

template <int I>
struct ignore
{
  template <typename T>
  ignore(T&&) // This constructor accepts anything
  {
  }
};

template <typename T>
struct nth_arg;

template <size_t... DropIndexes>
struct nth_arg<std::integer_sequence<size_t, DropIndexes...>>
{
  template <typename Arg, typename... Rest>
  static decltype(auto) get(ignore<DropIndexes>..., // ignore args 0...n-1
                            Arg&& arg,
                            Rest&&...) // also ignore the rest
  {
    return std::forward<Arg>(arg); // return nth arg
  }
};

And then call

template <int... Is, typename... Args>
static void call_zoo(indices<Is...>, Args&&... args)
{
  B<sizeof...(Is)>::zoo(nth_arg<std::make_index_sequence<Is>>::get(
      std::forward<Args>(args)...)...);
}

template <int M>
struct A
{
  typedef indices<0, 2, 3> IS;

  template <typename... Args>
  static void foo(Args... args)
  {
    static_assert(sizeof...(args) == M, "");
    call_zoo(IS{}, std::forward<Args>(args)...);
  }
};

If you're using C++11, you can easily roll your own integer_sequence.

like image 192
Rumburak Avatar answered Sep 27 '22 01:09

Rumburak


Pack the arguments into a tuple of references, and then retrieve them with std::get and a pack expansion on the indices.

template<class Tuple, int... Is>
static void magic(Tuple&& args, indices<Is...>){
    B<IS::N>::zoo(std::get<Is>(std::forward<Tuple>(args))...);
}

template <typename... Args>
static void foo(Args... args)
{
    static_assert(sizeof...(args) == M, "");
    magic(std::forward_as_tuple(args...), IS{});
}

(You may want to make foo take forwarding references.)

like image 44
T.C. Avatar answered Sep 27 '22 01:09

T.C.