Xaqq
Xaqq

Reputation: 4386

std::enable_if and std::shared_ptr

Goal: Having function dispatching working as intended. The minimal example code should speak for itself. I want to support tasks: Named task, that are implemented in their own class, and simpler task, specified using lambda. Ideally, anything that can be converted to std::function<void (void)> should work.

#include <iostream>
#include <memory>
#include <functional>

// A Base class for my tasks                                                                                                                                                                                                                 
class BaseTask
{
public:
  virtual void blah() = 0;
};

// An important enough tasks that it gets to have its                                                                                                                                                                                        
// own class.                                                                                                                                                                                                                                
class NamedTask : public BaseTask
{
public:
  virtual void blah()
  {
    std::cout << "Hey !" << std::endl;
  }
};

// A wrapper around simpler tasks. (lambda)                                                                                                                                                                                                    
class GenericTask : public BaseTask
{
public:
  GenericTask(const std::function<void (void)> fct) :
    fct_(fct) {}

  virtual void blah() override
  {
    fct_();
  }

private:
  std::function<void (void)> fct_;
};

void enqueue(std::shared_ptr<BaseTask> t)
{
  // store in queue.                                                                                                                                                                                                                         
  // We'll just call it here for the sake of the example                                                                                                                                                                                     
  t->blah();
}

template<typename Callable>
//typename std::enable_if<!std::is_base_of<BaseTask, Callable>::value>::type                                                                                                                                                                  
//typename std::enable_if<!std::is_base_of<std::shared_ptr<BaseTask>, Callable>::value>::type                                                                                                                                                 
void enqueue(const Callable &c)
{
  auto t = std::make_shared<GenericTask>(c);
  t->blah();
}

int main()
{
  auto named = std::make_shared<NamedTask>();
  enqueue(named); // doesn't compile: tries to call the templated enqueue.                                                                                                                                                                 

  enqueue([] () -> bool { std::cout << "Lamda" << std::endl; });
}

Problem: I don't manage to write a proper enable_if template. The commented lines in the example is what I tried.

Upvotes: 1

Views: 1083

Answers (1)

Barry
Barry

Reputation: 303277

You have two cases: your callable is convertible to shared_ptr<BaseTask>, or not. Checking the base is incorrect as shared_ptr<NamedTask> is not related by class hierarchy to shared_ptr<BaseTask>, but you can construct a shared_ptr<BaseTask> from it.

That is:

// is convertible to shared_ptr<BaseTask>
void enqueue(std::shared_ptr<BaseTask> t);

// is NOT convertible to shared_ptr<BaseTask>
template<typename Callable>
typename std::enable_if<
    !std::is_convertible<Callable, std::shared_ptr<BaseTask>>::value
>::type
enqueue(const Callable &c);

Alternatively, you can think of the two cases as constructible/not constructible:

template<typename Callable>
typename std::enable_if<
    !std::is_constructible<std::shared_ptr<BaseTask>, Callable>::value
>::type
enqueue(const Callable &c);

Third alternative is to condition the function template enqueue on anything that is callable with zero arguments:

template<typename Callable>
auto enqueue(const Callable &c) -> decltype(c(), void())

Upvotes: 3

Related Questions