Problem with creating a generic server call system

I was following a network code tutorial and noticed the bloat of functions having to be created due to the way Unreal requires additional functions for RPC calls, e.g.:



.h
void Jump();

UFUNCTION(Server, WithValidation, Reliable)
void ServerJump();
void ServerJump_Implementation() { Jump(); }
bool ServerJump_Validate() { return true; }

.cpp
void MyPawn::Jump()
{
	if (Role < ROLE_Authority) { ServerJump(); return; }
	// Otherwise handle jump as server.
}


Notice how the Jump() function calls ServerJump() which itself calls Jump(), but as the server. Seeing as how this was a repeating pattern I tried to abstract it out to a general solution:



.h
UFUNCTION(Server, WithValidation, Reliable)
template <typename Class, typename Ret, typename...Types>
Ret ServerCall(Class* Instigator, Ret(Class::*DelegatedFunc)(Types...), Types&&...Params);

template <typename Class, typename Ret, typename...Types>
Ret ServerCall_Implementation(Class* Instigator, Ret(Class::*DelegatedFunc)(Types...), Types&&...Params)
{
	return (Instigator->*DelegatedFunc)(std::forward<Types>(Params)...);
}

template <typename Class, typename Ret, typename...Types>
bool ServerCall_Validate(Class* Instigator, Ret(Class::*DelegatedFunc)(Types...), Types&&...Params)
{
	return true;
}


Seeing as how I attempted to reduce the problem to something generic, in theory I should just have to call this function anytime I want the above set of procedures to occur; call action, check if server, if not call on server version:



.cpp
void MyPawn::Jump()
{
	if (Role < ROLE_Authority) ServerCall(this, &MyPawn::Jump);
	// Handle jump as server.
}

Should be able to re-use ServerCall(...) with swimming, moving, speaking, etc without having to create extra server versions of everything. Can even pass as many parameters as I like due to variadic template.


Now while it seems simple enough I keep getting various errors that I can’t make sense of; the most recent is unrecognised type ‘template’.
Is there anything obvious that I’ve missed? I understand the code could probably be more efficient, but I’m surprised that it doesn’t even run.

Edit: The generic templates are all in the header for ‘MyPawn’ as I’m not sure where to keep them as I keep getting various errors depending on where I put them. Ideally they would be in their own source file such as ‘NetworkHelpers.cpp’, but when I attempted that I believe I got an unresolved external symbol error or some such.

Darn, I think according to the following link, UFUNCTION(…) doesn’t support templates types save for a few:
https://www.unrealengine.com/blog/unreal-property-system-reflection



Most common types work as expected, but the property system can’t represent all possible C++ types 
(notably only a few template types such as TArray and TSubclassOf are supported, and their template parameters cannot be nested types).


Does this mean UE4’s reflection system is incapable of supporting templated functions such as mine? If so, does anyone know if this will likely change in any upcoming version of the engine?

Thanks.

I have to say, love your approach :slight_smile:

Could you possibly define your own macro that takes care of most of it? Something like


#define DECLARE_SERVER_FUNCTION(name, implementation, validation) UFUNCTION(Server, WithValidation, Reliable) \
void Server##name(); \
void Server##name_Implementation() { ##implementation } \
bool Server##name_Validate() { ##validation }

Used like this: DECLARE_SERVER_FUNCTION(Jump, ( Jump(); ) , ( return true; ) )

Or alternatively, although probably not as nice and simple as your original attempt


class FunctionArguments
{
}

class FunctionResult
{
}

class Function
{
public:
	virtual void operator() (FunctionArguments* args, FunctionResult* ret) = 0;
}

UFUNCTION(Server, WithValidation, Reliable)
void ServerCall(Function* f, FunctionArguments* args, FunctionResult* ret);

void ServerCall_Implementation(Function* f, FunctionArguments* args, FunctionResult* ret)
{
	f->operator()(args, ret);
}

bool ServerCall_Validate(Function* f, FunctionArguments* args, FunctionResult* ret)
{
	return true;
}

Used like this:


class JumpFunction : public Function
{
public:
	virtual void operator() (FunctionArguments* args, FunctionResult* ret)
	{
		//do something (with or without args and ret)
	}

	static JumpFunction* s_pJumpFunction = new JumpFunction();
}

//...
//call the function somewhere, args and ret not used here
ServerCall(s_pJumpFunction, nullptr, nullptr);
//...

Warning: not tested!

Thanks for your reply. :smiley:

I spent a lot of today learning about #define since I’ve never had a good reason to do so and because it’s generally ill-advised to use it. Seems it’s the older brother to what templates are now in some sense, really interesting.
I still didn’t want to rely on it seeing as how it has its problems so I tried another approach. This new approach lacks the flexibility of my initial variadic template approach, but should reduce code duplication:



UFUNCTION(Server, WithValidation, Reliable)
void ReliableServerCall(void (ASpherePawn::*DelegateFunc)() );
void ReliableServerCall_Implementation(void (ASpherePawn::*DelegateFunc)() ) { (this->*DelegateFunc)(); }
bool ReliableServerCall_Validate(void (ASpherePawn::*DelegateFunc)() ) { return true; }


Unfortunately the reflection system doesn’t seem to support passing pointers to member functions as I’m getting an Unrecognised type ‘void’ error.
I think for the time being your #define solution is the way to go, thanks for sharing it!
I’m hoping someone from Epic could let me know if they plan to improve reflection support for templated and pointer to member function - functions.

Edit:

I can’t get the #define solution to work either:



.h
#define DECLARE_RELIABLE_SERVER_CALL(DelegateF)					\
UFUNCTION(Server, WithValidation, Reliable)							\
void ReliableServer_##DelegateF();								\
void ReliableServer_##DelegateF_Implementation() { ##DelegateF(); }         	\
bool ReliableServer_##DelegateF_Validate() { return true; }	
...
public:
DECLARE_RELIABLE_SERVER_CALL(Jump)
...
.cpp
ReliableServer_Jump();


The #define is outside my pawn class while the public: line is within it.
I get an 1 unresolved externals error. Any ideas?

Edit 2:

The problem is that the compiler is looking for a definition of ReliableServer_Jump(), but doesn’t find it. If I put a ‘{}’ in front of the function in the macro then it compiles. The problem now is that the jump implementation never runs as, I guess, Unreal never gets a chance to put its own code in there to run …_Implementation(). I think for the time being I will have to stick to boilerplate code and just let it bloat.