Reputation: 4386
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.
std::shared_ptr<NamedTask>
, which is not a child of BaseTask
.std::shared_ptr<NamedTask>
does not derive from std::shared_ptr<BaseTask>
.Upvotes: 1
Views: 1083
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