Home > Blockchain >  C conditional template member function
C conditional template member function

Time:10-05

I am trying to understand how to use std::enable_if to choose between 2 functions implementation. In this case, if the type TupleOfCallback doesn't contains all the type, it will not compile because std::get<...> will throw an error.

For exemple: Executor<Entity1*, Entity2*> task([](Entity1 *e){}, [](Entity2 *2){});

This will not compile because Entity3* is not part of the tuple.

It seem that we can choose between two functions with the same prototype,

void Exec(Entity3 *entity)
{
  //enabled when Entity3* is **not** in the tuple
}

OR

void Exec(Entity3 *entity)
{
  //enabled when Entity3 is in the tuple
  std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
}

But i dont understand how to achieve this goal.

C template mechanism is still hard for me, any help is welcome.

template<typename ...T>
class Executor
{
  typedef std::tuple<std::function<void(T)>...> TupleOfCallback;
public:
  Executor(const std::function<void(T)> &...func)
  {
  }

  void Exec(Entity1 *entity)
  {
    std::get<std::function<void(Entity1*)>>(m_Callbacks)(entity);
  }

  void Exec(Entity2 *entity)
  {
    std::get<std::function<void(Entity2*)>>(m_Callbacks)(entity);
  }

  void Exec(Entity3 *entity)
  {
    std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
  }

public:
    TupleOfCallback m_Callbacks;
};

CodePudding user response:

Building on this one Check if parameter pack contains a type. You can use two traits to select which method to call:

#include <iostream>
#include <type_traits>

struct Entity1 {};
struct Entity2 {};
struct Entity3 {};

template<typename What, typename ... Args>
struct is_present {
    static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};

template<typename T>
struct is_entity : is_present<T,Entity1,Entity2,Entity3> {};

template <typename T, typename ...Args>
struct is_present_entity {
    static constexpr bool value = is_present<T,Args...>::value && is_entity<T>::value;
};
template <typename T, typename ...Args>
struct is_not_present_entity {
    static constexpr bool value = (!is_present<T,Args...>::value) && is_entity<T>::value;
};


template<typename ...T>
class Executor
{
  
public:
    template <typename U, std::enable_if_t< is_present_entity<U,T...>::value,bool> = true>
    void Exec(U* t){
        std::cout << "foo\n";
    }
    template <typename U, std::enable_if_t< is_not_present_entity<U,T...>::value,bool> = true>
    void Exec(U* t){
        std::cout << "bar\n";
    }
};

struct foo {};

int main(void) {
   Executor<Entity1,Entity2> ex;
   Entity1 e1;
   ex.Exec(&e1);
   Entity3 e3;
   ex.Exec(&e3);
  // foo f;
  // ex.Exec(&f);
}

output:

foo
bar

CodePudding user response:

Another C 17 option:

template <typename T>
class ExecutorLeaf
{
public:
  std::function<void(T)> callback;

  void Exec(T entity) { callback(entity); }
};

template <typename... Ts>
class Executor : ExecutorLeaf<Ts>...
{
public:

  Executor(const std::function<void(Ts)>&...funcs) : ExecutorLeaf<Ts>{funcs}... {}

  using ExecutorLeaf<Ts>::Exec...; // C  17
  // Fallback
  template <typename T> void Exec(T) {}
};

Demo

CodePudding user response:

If you can guarantee that all types appear only once, then the following should work:

template<typename... Ts>
class Executor {
  using TupleOfCallback = std::tuple<std::function<void(Ts)>...>;
 public:
  Executor(const std::function<void(Ts)>&... func);

  template<class E>
  std::enable_if_t<(std::is_same_v<Ts, E*> || ...)>
  Exec(E* entity) {
    std::get<std::function<void(E*)>>(m_Callbacks)(entity);
  }
  
  template<class E>
  std::enable_if_t<!(std::is_same_v<Ts, E*> || ...)>
  Exec(E* entity) 
  { }

public:
  TupleOfCallback m_Callbacks;
};

The basic idea is to use fold-expression to detect whether E* is included in Ts..., thereby enabling the corresponding function.

Demo.

  • Related