Sounds like you want delegates?
In C# one can write
void ForEach<T>(Action<T> action)
{
for (int i = 0; i < MAX_BLOCK; i++)
for (int ii = 0; ii < MAX_BLOCK; ii++)
for (int iii = 0; iii < BUILDING_X; iii++)
for (int iiii = 0; iiii < BUILDING_Y; iiii++)
action(block[i][ii].building[iii][iiii]);
}
In C++ however, this is more complicated because of calling conventions (thiscall vs stdcall vs cdecl) but with some template magic it is absolutely possible.
What I do is using this together with some macro code/ multiple include code to fill the template arguments dynamically. I do this because I don't like to use all the new fancy C++14 and above stuff and rarely use C++11 features. The code here is fully C++99 compilant.
(If you instead want fancy C++ compiler magic, go ahead and skip the rest)
I have declared static inline functions that all share the same function signature. This way I can store any function regardless of it's calling convention and decorators (const) via a proxy function pointer in my delegate struct
template<typename ret do_if(ORDER, _separator) variadic_decl(typename Args, ORDER)> struct InstanceCallContext<ret (variadic_decl(Args, ORDER))>
{
public:
/**
Provides a class member function call context to use in dynamic delegate
*/
template<class T, ret (T::*type) (variadic_decl(Args, ORDER))> static force_inline ret Functor(void* target do_if(ORDER, _separator) variadic_args(Args, a, ORDER))
{
T* ptr = static_cast<T*>(target);
return (ptr->*type)(variadic_decl(a, ORDER));
}
/**
Provides an anonymous class member function call context to use for dynamic calling
*/
template<class T, ret (T::*type) (variadic_decl(Args, ORDER))> static force_inline void AnonymousFunctor(void* target, void** args)
{
T* ptr = static_cast<T*>(target);
*reinterpret_cast<typename SE::TypeTraits::Const::Remove<typename SE::TypeTraits::Reference::Remove<ret>::Result>::Result*>(args[ORDER]) = (ptr->*type)(variadic_deduce(Args, args, ORDER));
(void)args;
}
/**
Provides a const class member function call context to use in dynamic delegate
*/
template<class T, ret (T::*type) (variadic_decl(Args, ORDER)) const> static force_inline ret ConstFunctor(void* target do_if(ORDER, _separator) variadic_args(Args, a, ORDER))
{
T* ptr = static_cast<T*>(target);
return (ptr->*type)(variadic_decl(a, ORDER));
}
/**
Provides an anonymous const class member function call context to use for dynamic calling
*/
template<class T, ret (T::*type) (variadic_decl(Args, ORDER)) const> static force_inline void AnonymousConstFunctor(void* target, void** args)
{
T* ptr = static_cast<T*>(target);
*reinterpret_cast<typename SE::TypeTraits::Const::Remove<typename SE::TypeTraits::Reference::Remove<ret>::Result>::Result*>(args[ORDER]) = (ptr->*type)(variadic_deduce(Args, args, ORDER));
(void)args;
}
};
Because of the lack of variadic templates in C++99, I simply declared a macro to solve this for me
#define variadic_args(type_name, args_name, count) JOIN(XP_VARIADIC_PARAMS_INVOKE_ARGSVAL, count)(type_name, args_name, _separator)
#define XP_VARIADIC_PARAMS_INVOKE_ARGSVAL0(type_name, args_name, separator)
#define XP_VARIADIC_PARAMS_INVOKE_ARGSVAL1(type_name, args_name, separator) JOIN(type_name, 0) JOIN(args_name, 0)
#define XP_VARIADIC_PARAMS_INVOKE_ARGSVAL2(type_name, args_name, separator) XP_VARIADIC_PARAMS_INVOKE_ARGSVAL1(type_name, args_name, separator) separator JOIN(type_name, 1) JOIN(args_name, 1)
It expands the provided parameters and repeats them while adding the iteration count.
Finally my delegate struct, also a variadic template
template<typename ret do_if(ORDER, _separator) variadic_decl(typename Args, ORDER)> struct DynamicDelegate<ret(variadic_decl(Args, ORDER))>
{
public:
typedef ret ReturnValue;
typedef ret (*FunctionPointer) (variadic_decl(Args, ORDER));
typedef ret (*ContextPointer) (void* do_if(ORDER, _separator) variadic_decl(Args, ORDER));
/**
A pointer to the internal call context
*/
force_inline ContextPointer Context() { return context; }
/**
An instance pointer when initialized to a member function, null_ptr otherwise
*/
force_inline void* Target() { return target; }
/**
Copy constructor
*/
force_inline DynamicDelegate(DynamicDelegate const& delegate) : context(delegate.context), target(delegate.target)
{ }
/**
Default constructor
*/
force_inline DynamicDelegate() : context(se_null), target(se_null)
{ }
/**
Class constructor initializes this context with given values
*/
force_inline DynamicDelegate(ContextPointer context, void* target) : context(context), target(target)
{ }
/**
Class destructor
*/
force_inline ~DynamicDelegate()
{ }
force_inline DynamicDelegate<ret(variadic_decl(Args, ORDER))>& operator=(DynamicDelegate<ret(variadic_decl(Args, ORDER))> const& delegate)
{
Bind(delegate.context, delegate.target);
return *this;
}
force_inline operator bool() const { return context != se_null; }
force_inline bool operator!() const { return !(operator bool()); }
force_inline ret operator()(variadic_args(Args, a, ORDER)) const
{
return Invoke(variadic_decl(a, ORDER));
}
/**
Binds the delegate to a new target
*/
force_inline void Bind(ContextPointer ctx, void* instance)
{
context = ctx;
target = instance;
}
/**
Binds the delegate to a new target
*/
force_inline void Bind(ContextPointer ctx)
{
Bind(ctx, se_null);
}
/**
Calls the function this context is bound to
*/
force_inline ret Invoke(variadic_args(Args, a, ORDER)) const
{
return context(target do_if(ORDER, _separator) variadic_decl(a, ORDER));
}
private:
ContextPointer context;
void* target;
};
encapsulates the proxy call pointer together with an optional pointer to an instance of the member it was created for. On invocation, the pointer and arguments are passed to the proxy and indirected to the member function call in the proxy function. I stored the member instance in my delegate class to be able to also forward to static functions and not just member functions but you won't need this I guess.
However, using the InstanceCallContext proxy functions, you could rewrite your code to accept some variadic function pointers
template<complicated macro code here> inline void ForEach(MyPtr action, [optional] variadic args)
{
for (int i = 0; i < MAX_BLOCK; i++)
for (int ii = 0; ii < MAX_BLOCK; ii++)
for (int iii = 0; iii < BUILDING_X; iii++)
for (int iiii = 0; iiii < BUILDING_Y; iiii++)
action(block[i][ii].building[iii][iiii], args);
}
ForEach(InstanceCallContext<void ()>::Functor<MyClass, MyClass::DoStuff>, myArgs);
I know InstanceCallContext is not as cool as C#'s actions but the minimal code requeired to perform this action. I tried a lot of different approaches but this is the best I achieved so far for executing dynamic functions