【问题标题】:How to achieve variadic virtual member function如何实现变参虚成员函数
【发布时间】:2018-05-13 12:48:40
【问题描述】:

所以我有这个功能...

virtual void CallRemoteFunction( const char* pServerGameObjectId, const char* pFunctionName, OVariant arg1 = OVariant(), OVariant arg2 = OVariant(), OVariant arg3 = OVariant(), OVariant arg4 = OVariant(), OVariant arg5 = OVariant(), OVariant arg6 = OVariant(), OVariant arg7 = OVariant(), OVariant arg8 = OVariant(), OVariant arg9 = OVariant() );

我决定重写这个函数,坦率地说,我对此感到尴尬。该函数很简单......接受可变数量的未知类型的参数并做一些事情。

我对现代 C++ 相当陌生,所以我进行了一些搜索,并假设我会找到一些简单/优雅的新方法。我想像...

//hypothetical code
virtual void CallRemoteFunction( const char* pServerGameObjectId, const char* pFunctionName, ... args )
{
    std::vector<OVariant> argsArray;
    for (auto& arg : args )
    {
        argsArray.push_back(arg)
    }

    //do other stuff
 }
 //end hypothetical code

但在我的搜索中,我找不到任何接近的东西。那么任何人都可以给我一些关于如何将我的原始功能重构为更清洁或更简单的想法吗?注意:解决方案必须是 C++ 11 或更早版本。

-更新- 该函数不必是虚拟的。
我希望能够像这样调用函数...

CallRemoteFunction("serverID","someFunc",1,2,3);

这里是参考的实现

//calls function on client if this peer is a linked server or vice versa
void Peer::CallRemoteFunction( const char* pServerGameObjectId,  const char* 
pFunctionName, OVariant arg1, OVariant arg2, OVariant arg3, OVariant arg4, 
OVariant arg5, OVariant arg6, OVariant arg7, OVariant arg8, OVariant arg9 )
{
    Command command;
    command.SetObjectName( pServerGameObjectId );
    command.SetFunctionName( pFunctionName );
    command.ResetArgs();
    if ( arg1.IsValid() ){ command.PushArg( arg1 ); }
    if ( arg2.IsValid() ){ command.PushArg( arg2 ); }
    if ( arg3.IsValid() ){ command.PushArg( arg3 ); }
    if ( arg4.IsValid() ){ command.PushArg( arg4 ); }
    if ( arg5.IsValid() ){ command.PushArg( arg5 ); }
    if ( arg6.IsValid() ){ command.PushArg( arg6 ); }
    if ( arg7.IsValid() ){ command.PushArg( arg7 ); }
    if ( arg8.IsValid() ){ command.PushArg( arg8 ); }
    if ( arg9.IsValid() ){ command.PushArg( arg9 ); }

    LOG_DEBUG( "Calling Remote Function : " << pServerGameObjectId << "." << 
command.GetFunctionName() );
    ByteArray buffer( command.GetSerializeSize() );
    command.Serialize( buffer );

    ZMQMessage* request = new ZMQMessage();//deleted when sent via zmq
    request->addmem( buffer.GetBytes(), buffer.m_NumBytes );

    PushMessage( MDPW_REQUEST, m_pServiceId, request );
}

【问题讨论】:

  • 好吧,你会接受 C 变量吗? :P
  • 问题出在virtual 那里。 C++ 可变参数(=可变参数模板)基于模板,不与virtual混合。
  • 听起来你可能想要 std::initializer_list
  • @Rakete1111,您能否详细说明如何使用 C 可变参数来完成此操作?不知道 args 的数量怎么办?您指的是可以用来查找大小的宏魔术吗?注意这个函数实际上是通过宏调用的。
  • @DannyDiaz(Jftr 和未来的读者,不要实际上做那种 C 可变参数的东西。使用你接受的答案。)

标签: c++ c++11 parameters variadic-functions


【解决方案1】:

当然,如果你愿意,你可以拥有它。您只需要将模板化转发器与非模板化虚函数结合起来。

我正在使用 gsl::span ("What is a "span" and when should I use one?") 和自动的 std::array (不是原生的,以避免 0 的特殊情况)以获得最佳的通用性、稳定性和效率。

virtual void DoCallRemoteFunction(
    const char* pServerGameObjectId,
    const char* pFunctionName,
    gsl::span<OVariant> args
) {
    ...
}

template<class... ARGS>
void CallRemoteFunction(
    const char* pServerGameObjectId,
    const char* pFunctionName,
    ARGS&&... args
) {
    std::array<OVariant, sizeof...(ARGS)> arr = { OVariant{std::forward<ARGS>(args)} ... };
    DoCallRemoteFunction(pServerGameObjectId, pFunctionName, arr);
}

我给它们起了不同的名字,因为你大概会覆盖虚函数,并且可能不希望 API 函数被遮蔽。

【讨论】:

  • 全部发送!我插上它,它工作。我没有我想要的那么简单,但我仍然喜欢它,我不再尴尬;)
  • 使用初始化列表强制 const; span 和堆栈上的数组可能会授予移动语义。
  • @KillzoneKid 请注意原始代码与您链接的代码之间的重要区别:virtual 的存在/不存在。
  • @Yakk-AdamNevraumont 是的。如果只允许原生数组大小为 0。哦,好吧,std::array 也不是很麻烦。
【解决方案2】:

如果你愿意接受稍微不同的调用语法(例如std::max also uses),你可以有一个非常优雅的解决方案:

virtual void CallRemoteFunction( const char* pServerGameObjectId, const char* pFunctionName, std::initializer_list<QVariant> args )
{
    std::vector<OVariant> argsArray;
    for (auto& arg : args )
    {
        argsArray.push_back(arg)
    }

    //do other stuff
}

这样称呼:

CallRemoteFunction("foo", "bar", { arg1, arg2, arg3 });

【讨论】:

  • tx Angew 供您快速响应。是的,我看着这样做。但目前,我不必更改我的调用语法。尽管该功能很丑陋,但它可以正常工作。还是保持原样吧?
  • @DannyDiaz 请参阅Deduplicator's answer 了解如何包装它以保持您当前的调用语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 2017-02-22
  • 1970-01-01
  • 1970-01-01
  • 2021-06-02
  • 1970-01-01
  • 2015-07-03
相关资源
最近更新 更多