passing a parameter pack over a legacy function signature using forward_as_tuple
Asked Answered
W

2

0

In my app I would like to pass in a parameter pack over a legacy function signature, and change the values. Here is code that illustrates my question with my attempts as comments:

#include <tuple>
#include <cassert>

void LegacySignature( void* param );

template< typename... ArgsT >
// using ???; // attempt: can 'template alias' or 'using declaration' make the pack's type visible so I can use it inside the LegacyFunction?
void MyFunc( ArgsT&... args )
{
  auto userArgsTuple = std::forward_as_tuple< ArgsT&... >( args... );

  LegacySignature( &userArgsTuple );
}

void LegacySignature( void* param )
{
//  auto userArgsTuple = reinterpret_cast<???>( param ); // attempt: how can I get the parameter pack's type declared so I can use it here?

  // do something with the params like change num to 44 and tf to true;
  //userArgsTuple->num = 44; // desired functionality
  //userArgsTuple->tf = true; // desired functionality
}

int main()
{
  int num { 33 };
  bool tf { false };
  MyFunc( num, tf );
  assert( num == 44 && tf == true );

  return 0;
}

Is there a way to make the parameter pack a declarable lvalue?

Wilinski answered 13/12, 2016 at 22:22 Comment(3)
As a guess, they want you to pass a callback. The callback takes a void* and some other args they provide. You want to pass some data through the void* so it is passed back to you at the other end. They won't store your callback or your void* longer than a fixed scope you have control over. Is this correct? Question: is the void* passed as the first, or last, parameter to your callback function?Irrefrangible
Using void* was merely my attempt at representing the actual signature as a neutral type for discussion’s sake. In fact the actual signature parameter is the venerable LPARAM which is a long. As MS states, both WPARAM and LPARAM are “Types use for passing & returning polymorphic values”. As such, no Callbacks are being passed or invoked in my question. My interest lies directly in a better understanding of how to forward an arbitrary number of arguments across such a legacy construct, where these parameters are value-changeable references in a smart pointer.Wilinski
FYI, I solved the issue of Callbacks over legacy signatures using future See StackOverflow issue [How can I use shared_ptr using PostThreadMessage?]( stackoverflow.com/questions/25667226) and look for the Answer that begins with @Remy Lebeau and @Wilinski have so far submitted two answers to my original posting….Wilinski
W
0

The code below fixes the sample code so that it answers the question as to how to pass a parameter pack over a legacy function signature using forward_as_tuple.

#include <tuple>
#include <cassert>
#include <memory>
#include <functional>

#define ARGSET int, bool

void LegacySignature( long* param ); // ie, LPARAM

template< typename... ArgsT >
struct MyParams
{
  MyParams( ArgsT... args ) : rvalRefs { std::forward_as_tuple( args... ) } {} // The resulting forward_as_tuple tuple has rvalue reference data members 
  std::tuple< ArgsT... > rvalRefs;
};


void LegacySignature( long* legSigParam )
{
  auto userArgsTuple( reinterpret_cast< MyParams< ARGSET >* >( legSigParam ) );

  // do something with the params like change num to 44 and tf to true;
  std::get< 0 >( userArgsTuple->rvalRefs ) = 44; // index types can probably be worked out using enums
  std::get< 1 >( userArgsTuple->rvalRefs ) = true;
}

int main()
{
  int num { 33 };
  bool tf { false };
  MyParams< ARGSET > myParams( num, tf );

  std::unique_ptr< MyParams< ARGSET > > legSigParamPtr = std::make_unique< MyParams< ARGSET > >( myParams );
  LegacySignature( ( long* )legSigParamPtr.get() );
  assert( std::get< 0 >( legSigParamPtr->rvalRefs ) == 44 && std::get< 1 >( legSigParamPtr->rvalRefs ) == true );

  return 0;
}
Wilinski answered 15/12, 2016 at 17:25 Comment(0)
I
0

I'm assuming what you want is a function pointer to your legacy signature.

Here is a C++11 approach.

template<class Sig, class F>
struct magic_callback_t;

template<class R, class...Args, class F>
struct magic_callback_t<R(Args...), F> {
  F f;
  void* pvoid() const { return this; }
  using result_sig = R(*)(void*, Args...);
  result_sig pfunc() const {
    return [](void* pvoid, Args...args)->R{
      auto* self = static_cast<magic_callback_t*>(pvoid);
      return (self->f)(std::forward<Args>(args)...);
    };
  }
};
template<class Sig, class F>
magic_callback_t<Sig, F> magic_callback( F&& f ) {
  return {std::forward<F>(f)};
}

Now we just do this:

auto callback = magic_callback( [&](){
  // use whatever as if we where in the enclosing scope
});

void(*)(void*) legacy_ptr = callback.pfunc();
legacy_ptr( callback.pvoid() );

will call the lambda you passed to magic_callback.

If you want to store stuff as a tuple, you can. Just capture the tuple in the lambda, then use std::get to access it in the body of the lambda. Use mutable if you want it to be mutable.

Irrefrangible answered 13/12, 2016 at 22:39 Comment(1)
Excellent callback solution. My basic need, however, is to switch threads, synchronously execute an existing function in the destination thread, perhaps with passed in parameters, and return the values synchronously. My SendThreadMessage solution I pointed you to does this, all except for the multiple parameter passing. The arbitrary-parameter-passing-by-reference approach as outlined in the sample code is a better design. Callback solutions are unfortunately asynchronous. I could always pass in a raw ptr to a unique or shared obj, but that would make the callers’ dev work more difficult.Wilinski
W
0

The code below fixes the sample code so that it answers the question as to how to pass a parameter pack over a legacy function signature using forward_as_tuple.

#include <tuple>
#include <cassert>
#include <memory>
#include <functional>

#define ARGSET int, bool

void LegacySignature( long* param ); // ie, LPARAM

template< typename... ArgsT >
struct MyParams
{
  MyParams( ArgsT... args ) : rvalRefs { std::forward_as_tuple( args... ) } {} // The resulting forward_as_tuple tuple has rvalue reference data members 
  std::tuple< ArgsT... > rvalRefs;
};


void LegacySignature( long* legSigParam )
{
  auto userArgsTuple( reinterpret_cast< MyParams< ARGSET >* >( legSigParam ) );

  // do something with the params like change num to 44 and tf to true;
  std::get< 0 >( userArgsTuple->rvalRefs ) = 44; // index types can probably be worked out using enums
  std::get< 1 >( userArgsTuple->rvalRefs ) = true;
}

int main()
{
  int num { 33 };
  bool tf { false };
  MyParams< ARGSET > myParams( num, tf );

  std::unique_ptr< MyParams< ARGSET > > legSigParamPtr = std::make_unique< MyParams< ARGSET > >( myParams );
  LegacySignature( ( long* )legSigParamPtr.get() );
  assert( std::get< 0 >( legSigParamPtr->rvalRefs ) == 44 && std::get< 1 >( legSigParamPtr->rvalRefs ) == true );

  return 0;
}
Wilinski answered 15/12, 2016 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.