Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ generic way to define multiple functions with a template

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);
}
like image 478
Cethric Avatar asked Apr 29 '18 01:04

Cethric


2 Answers

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
 }
like image 199
max66 Avatar answered Sep 22 '22 13:09

max66


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:

  1. This inheritance is an implementation-detail and something you'd want to hide from clients.
  2. *Now we can easily specialize our 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.

Demo

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.

Tag dispatch demonstration

like image 34
AndyG Avatar answered Sep 19 '22 13:09

AndyG