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?

rtischer8277
  • 496
  • 6
  • 27
  • 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? – Yakk - Adam Nevraumont Dec 13 '16 at 22:32
  • 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. – rtischer8277 Dec 14 '16 at 14:15
  • FYI, I solved the issue of Callbacks over legacy signatures using `future` See StackOverflow issue [How can I use shared_ptr using PostThreadMessage?]( http://stackoverflow.com/questions/25667226) and look for the Answer that begins with *@Remy Lebeau and @rtischer8277 have so far submitted two answers to my original posting…*. – rtischer8277 Dec 14 '16 at 14:17

2 Answers2

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 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. – rtischer8277 Dec 14 '16 at 17:16
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;
}
rtischer8277
  • 496
  • 6
  • 27