C++ Convert tuple of homogeneous wrapped type to tuple of raw type

I'd like to call std::apply() to a function; however, I am unable to because the std::tuple I use is currently wrapped. For example:

#include <tuple>

template <class T>
struct wrapped
{
    wrapped(T t) : t(t) {}

    T t;
};

template <class T, class ... Args>
struct delay_call
{
    T(*callback)(Args...);
    std::tuple<Args...> params;

    delay_call(T(*callback)(Args...), Args ... params) :
        callback(callback), params(params...)
    {}

    T call()
    {
        return std::apply(callback, params);
    }
};

template <class T, class ... Args>
struct delay_call_with_wrap
{
    T(*callback)(Args...);
    std::tuple<wrapped<Args>...> w_params;

    delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
        callback(callback), w_params(w_params...)
    {}

    T call()
    {
        std::tuple<Args...> params; // = w_params.t
        return std::apply(callback, actual_params);
    }
};

float saxpy(float a, float x, float y)
{
    return a * x + y;
}

int main()
{
    float a = 1, x = 2, y = 3;
    delay_call delay_saxpy(saxpy, a, x, y);

    wrapped w_a = 1.f, w_x = 2.f, w_y = 3.f;
    delay_call_with_wrap w_delay_saxpy(saxpy, w_a, w_x, w_y);

    float result = delay_saxpy.call();

    float w_result = w_delay_saxpy.call();
}

the delay_call struct works as expected; however, I am unsure how to go about extracting the actual value of each tuple element and giving that to std::apply() to execute.

In short, for delay_call_with_wrap::call, how would I convert std::tuple<wrapped<Args>...> to a std::tuple<Args...>?

I would avoid std::apply completely and call the callback directly by unpacking the tuple using std::index_sequence:

template <std::size_t ...I> T call_low(std::index_sequence<I...>)
{
    return callback(std::get<I>(w_params).t...);
}

T call()
{
    return call_low(std::make_index_sequence<sizeof...(Args)>{});
}

C (programming language), C programming is a general-purpose, procedural, imperative computer programming language developed in 1972 by Dennis M. Ritchie at the Bell Telephone  C or c is the third letter in the English and ISO basic Latin alphabets. Its name in English is cee (pronounced / ˈsiː /), plural cees.

In short, for delay_call_with_wrap::call, how would I convert std::tuple<wrapped<Args>...> to a std::tuple<Args...>?

It seems to me is better avoid std::apply() using the old std::make_index_sequence/std::index_sequence way (see HolyBlackCat answer).

But, if you really want to use std::apply(), you can call it a first time to unwrap the tuple (to get a tuple of unwrapped values) and then call is as usual.

I mean something as

T call ()
 {
   auto actual_params = std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
      w_params);
   return std::apply(callback, actual_params);
}

or, in a single call, directly

T call()
 {
   return std::apply(callback,
             std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
                w_params));
 }

This solution is reasonable, IMHO, if the w_param member is constant so you can calculate the actual_params one time for all and make it static

C Programming Tutorial for Beginners, C Language - Overview - C is a general-purpose, high-level language that was originally developed by Dennis M. Ritchie to develop the UNIX operating system​  Stock analysis for Citigroup Inc (C:New York) including stock price, stock chart, company news, key statistics, fundamentals and company profile.

Probably not the best solution for use in practice, but here's one using a variadically templated lambda to avoid index_sequence:

template <class T, class ... Args>
struct delay_call_with_wrap
{
    T(*callback)(Args...);
    std::tuple<wrapped<Args>...> w_params;

    delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
        callback(callback), w_params(w_params...)
    {}

    T call()
    {
        auto helper = [this] <class ... Args_> (wrapped<Args_>... args)
        {
            return callback(args.t...);
        };
        return std::apply(helper, w_params);
    }
};

Demo

The idea is to just provide a function that matches the arguments that std::apply yields here - it needs to take wrapped<Args>.... From there it's trivial to expand the pack while extracting the wrapped value.

We use a lambda because std::apply wants a Callable, so we can't just use another member function. Well, I guess we could overload operator() for delay_call_with_wrap. That would be mildly confusing but at least not limited to C++2a (and missing compiler support) like templated lambdas.

"C" Programming Language: Brian Kernighan, C is a powerful general-purpose programming language. It can be used to develop software like operating systems, databases, compilers, and so on. C-- (pronounced cee minus minus) is a C -like programming language. Its creators, functional programming researchers Simon Peyton Jones and Norman Ramsey, designed it to be generated mainly by compilers for very high-level languages rather than written by human programmers.

Learn C, The best site for C and C++ programming. Popular, beginner-friendly C and C++ tutorials to help you become an expert! This is a list of operators in the C and C++ programming languages.All the operators listed exist in C++; the fourth column "Included in C", states whether an operator is also present in C. Note that C does not support operator overloading.. When not overloaded, for the operators &&, ||, and , (the comma operator), there is a sequence point after the evaluation of the first operand.

C Tutorial, C is a computer programming language. That means that you can use C to create lists of instructions for a computer to follow. C is one of thousands of  As well as C and Simula's influences, other languages also influenced this new language, including ALGOL 68, Ada, CLU and ML . Initially, Stroustrup's "C with Classes" added features to the C compiler, Cpre, including classes, derived classes, strong typing, inlining and default arguments.

C Language - Overview, C++ is nearly a superset of C language (There are few programs that may compile in C, but not in C++). Beginning with C programming: Structure of a C program Microsoft C++, C, and Assembler documentation. Learn how to use C++, C, and assembly language to develop applications, services, and tools for your platforms and devices.

Comments
  • Appreciate the solution with std::apply even though the old solution is more suitable after all. Additionally w_params are not constant for my use case unfortunately, so the actual params of type std::tuple<Args...> cannot be calculated in one go.
  • @MichaelChoi - I'm agree: the best solution, IMHO, is the one in HolyBlackCat's answer.
  • I see... I didn't realize lambdas could be templatized in C++17. This solution would be easiest to implement in my opinion.
  • @MichaelChoi Oh sorry, I missed the C++17 tag. Templated lambdas only exist in C++20 (and, as shown, don't have good compiler support at all yet). But if they were a stable part of the language this would be a great use case.