In C++ is it possible to define multiple methods based of the number of template parameters provided? Similar to how variadic functions work?
With functions I can do
template <class ...Args>
struct VariadicFunctionCallback {
typedef std::function<void(std::shared_ptr<Args>...)> variadic;
};
But what I want to know is if I could do something similar but to create multiple functions instead of multiple arguments
template <class ...FunctionArg>
class Example {
void Function(FunctionArg)...
}
Which would then allow me to do something like
template <>
class Example<int, float> {
void Function(int i) {
...
}
void Function(float f) {
...
}
}
And if this is possible what are the advantages over my current setup which is like
template<class EventType>
class EventHandler {
public:
void HandleEvent(const std::shared_ptr<EventType>& event) {
}
};
class ExampleEvent : public Event<ExampleEvent> {
};
class ExampleHandler : public EventHandler<ExampleHandler>, EventHandler<Events::ShutdownEvent> {
public:
void HandleEvent(const std::shared_ptr<ExampleEvent> &event);
void HandleEvent(const std::shared_ptr<Events::ShutdownEvent> &event);
};
--Edit-- I ended up with a mix if the two solutions. It is probably not the best and I will continue to play around with and improve it overtime.
template <class EventType>
class BaseEventHandler {
public:
EventIdentifier GetIdentifier() const {
return EventType::GetIdentifier();
}
virtual void HandleEvent(const std::shared_ptr<EventType> &event) = 0;
};
template<class EventType, class ...EventTypes>
class EventHandler: public BaseEventHandler<EventTypes>... {
};
Which then allows me to do
class EventListener: public EventHandler<ShutdownEvent, MousePosEvent, WindowCloseRequestEvent> {
void HandleEvent(const std::shared_ptr<ShutdownEvent> &event);
void HandleEvent(const std::shared_ptr<MousePosEvent> &event);
void HandleEvent(const std::shared_ptr<WindowCloseRequestEvent> &event);
}
I suppose you can make Example
a sort of recursive self-inheritancing class; something as
template <typename ...>
struct Example
{
// dummy Function() to end the recursion
void Function ()
{ }
};
template <typename T0, typename ... Ts>
struct Example<T0, Ts...> : public Example<Ts...>
{
using Example<Ts...>::Function;
void Function (T0 const &)
{ };
};
So you can write
int main ()
{
Example<int, long, float> e0;
e0.Function(0);
e0.Function(0L);
e0.Function(0.0f);
}
-- EDIT --
The OP ask
Could a specialisation then be preformed on top of this?
Do you mean something as follows?
template <typename ...>
struct Example
{
// dummy Function() to end the recursion
void Function ()
{ }
};
template <typename T0, typename ... Ts>
struct Example<T0, Ts...> : public Example<Ts...>
{
using Example<Ts...>::Function;
void Function (T0 const &)
{ };
};
template <typename ... Ts>
struct Example<float, Ts...> : public Example<Ts...>
{
void FunctionFloat (float const &)
{ };
};
int main ()
{
Example<int, long, float> e0;
e0.Function(0);
e0.Function(0L);
e0.FunctionFloat(0.0f);
//e0.Function(0.0f); // compilation error
}
This answer start's with max66's answer, more or less
We start with a class that uses recursive inheritance to implement our function. In my case, I chose operator()
, and I used a variadic using declaration to bring all children operator()
into scope:
namespace detail{
template<class T, class... U>
struct ExampleImpl : ExampleImpl<U>...{
using ExampleImpl<U>::operator()...;
void operator()(T _arg){/*...*/}
};
}
Where my answer diverges from max66's is in that we'll use this ExampleImpl
class to compose our Example
class:
template<class... T>
class Example
{
public:
template <class U>
void Function(U arg)
{
impl(arg);
}
void Function(float arg)
{
/*Your specialization code*/
}
private:
detail::ExampleImpl<T...> impl;
};
I do it this way for 2 reasons:
Function
function for whatever type we want because we always have a choice in whether to call through to our instance of ExampleImpl
or not.If ExampleImpl
needs to use member variables of your Example
class, then you can either turn ExampleImpl
into a full-fledged PIMPL class, or modify its constructor or operator()
to accept additional arguments as a form of Dependency Injection
*You could just as easily perform full class specialization where float
is one of the template parameters in the specialization, and define your own Function
. Or you could use a form of tag dispatching to hide the float
version unless it was in the list of template types.
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