So I had this function...
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() );
I decided to rewrite the function because, frankly, I was embarrassed by it. The function is straightforward... take a variable number of arguments of unknown type and do stuff.
I am fairly new to modern C++, so I did some searching and assumed I would find some simple/elegant new way of doing this. I imagined something like...
//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
But in my searches, I could not find anything close. So could anybody kindly give me some ideas on how to refactor my original function into something cleaner or simpler? Note: the solution must be C++ 11 or older.
-Update-
The function does not have to be virtual.
I want to be able to call the function like so...
CallRemoteFunction("serverID","someFunc",1,2,3);
Here is the implementation for reference
//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 );
}
Sure, you can have that, if you want. You just have to combine a templated forwarder with a non-templated virtual function.
I'm using gsl::span
("What is a "span" and when should I use one?") and an automatic std::array
(not a native one to avoid the special case of 0) for best generality, stability and efficiency.
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);
}
I have given them different names because you presumably will override the virtual function, and probably don't want the API-function to be shadowed then.
If you're willing to accept slightly different call syntax (which e.g. std::max
also uses), you can have a very elegant solution:
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
}
Called like this:
CallRemoteFunction("foo", "bar", { arg1, arg2, arg3 });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With