【问题标题】:passing a parameter pack over a legacy function signature using forward_as_tuple使用 forward_as_tuple 在遗留函数签名上传递参数包
【发布时间】:2017-04-29 03:10:35
【问题描述】:

在我的应用程序中,我想通过旧函数签名传递参数包,并更改值。以下代码通过我作为 cmets 的尝试来说明我的问题:

#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;
}

有没有办法让参数包成为可声明的左值?

【问题讨论】:

  • 作为猜测,他们希望您传递回调。回调采用void* 和它们提供的其他一些参数。您想通过void* 传递一些数据,以便在另一端传回给您。他们不会将您的回调或void* 存储的时间长于您可以控制的固定范围。它是否正确?问题:void* 是作为第一个或最后一个参数传递给回调函数的吗?
  • 使用void* 只是为了讨论,我试图将实际签名表示为中性类型。事实上,实际的签名参数是古老的LPARAM,即long。正如 MS 所说,WPARAMLPARAM 都是“用于传递和返回多态值的类型”。因此,在我的问题中没有传递或调用回调。我的兴趣直接在于更好地理解如何在这种遗留结构中转发任意数量的参数,其中这些参数是智能指针中的值可变引用。
  • 仅供参考,我使用future 解决了对遗留签名的回调问题,请参阅 StackOverflow 问题 [如何使用 PostThreadMessage 使用 shared_ptr?](stackoverflow.com/questions/25667226) 并查找以 @Remy Lebeau 和 @rtischer8277 到目前为止已经为我的原始帖子提交了两个答案…….

标签: c++11 parameters parameter-passing using-declaration template-aliases


【解决方案1】:

下面的代码修复了示例代码,以便回答有关如何使用 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;
}

【讨论】:

    【解决方案2】:

    我假设你想要的是一个指向旧签名的函数指针。

    这是一种 C++11 方法。

    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)};
    }
    

    现在我们这样做:

    auto callback = magic_callback( [&](){
      // use whatever as if we where in the enclosing scope
    });
    
    void(*)(void*) legacy_ptr = callback.pfunc();
    legacy_ptr( callback.pvoid() );
    

    将调用您传递给 magic_callback 的 lambda。

    如果您想将内容存储为元组,您可以。只需捕获 lambda 中的元组,然后使用 std::get 在 lambda 主体中访问它。如果您希望它是可变的,请使用 mutable

    【讨论】:

    • 优秀的回调解决方案。然而,我的基本需求是切换线程,同步执行目标线程中的现有函数,可能带有传入的参数,并同步返回值。我的 SendThreadMessage 解决方案我指出你这样做,除了多参数传递。示例代码中概述的任意参数传递引用方法是一种更好的设计。不幸的是,回调解决方案是异步的。我总是可以将原始 ptr 传递给唯一或共享的 obj,但这会使调用者的开发工作更加困难。
    猜你喜欢
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多