SFINAE

Started by
3 comments, last by TheUnbeliever 12 years, 6 months ago
I have something that looks like:

[source lang="cpp"]
template <typename T>
struct Traits
{
typedef BOOST_TYPEOF(&T::operator()) MemOpPtr;
// ... boost::function_types and mpl magic ...
};

template <typename O, typename I>
struct Traits<O (*)(I)>
{ /* ... */ };

// overload A
template <typename T> void func(const T& o)
{ /* uses Traits<T> */ }

// overload B - binds a boost::function holding T::operator() to p
template <typename T> void func(const boost::shared_ptr<T>& p)
{ /* uses Traits<T> */ }[/source]

How can I have overload B be used when A's Traits substitution fails? I can just rename the second overload, but would prefer to keep the same syntax if possible. At the moment, I'll get errors telling me that operator() isn't a member of boost::shared_ptr. BOOST_TYPEOF (alone) doesn't give SFINAE, but I think something like enable_if< is_defined<T::operator()> > on the default Traits would suffice?

Thanks.
[TheUnbeliever]
Advertisement
Off the top of my head, so the concept should work even if the strict syntax doesn't ;-)

template <typename T>
void func(const T& o, const T::MemOpPtr& = T::MemOpPtr())
{
// ...
}



Substitution will fail if T does not provide the MemOpPtr typedef, and fall back on the second overload.

I think.

You might have to do some trickery with fake variadic functions to get all compilers to do this cleanly, I can't remember offhand.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Hm, but I've already got something similar, albeit in the return type. Sorry, I'll post my actual code:

[source lang="cpp"]namespace ppl
{
template <typename T>
struct StageTraits
{
typedef BOOST_TYPEOF(&T::operator()) MemOpPtr;

typedef typename boost::function_types::result_type<MemOpPtr>::type O;
typedef typename boost::mpl::at_c<boost::function_types::parameter_types<MemOpPtr>, 1>::type I;

typedef boost::function<O (I)> F;
};

template <typename O, typename I>
struct StageTraits< boost::function<O (I)> >
{ typedef boost::function<O (I)> F; };

template <typename O, typename I>
struct StageTraits<O (*)(I)>
{ typedef boost::function<O (I)> F; };

template <typename T>
detail::List<typename StageTraits<T>::F, detail::Empty> stage(const T& f)
{ return detail::List<typename StageTraits<T>::F, detail::Empty>(typename StageTraits<T>::F(f)); }

template <typename T>
detail::List<typename StageTraits<T>::F, detail::Empty> stage(const boost::shared_ptr<T>& f_obj)
{
typename StageTraits<T>::F f(boost::bind(&T::operator(), f_obj, _1));
return detail::List<typename StageTraits<T>::F, detail::Empty>(f);
}
}[/source]

Example usage:
[source lang="cpp"]int inc(int x) { return x+1; }
int mul(int a, int b) { return a*b; }

struct Add
{
int d;

Add(int d)
: d(d)
{}

int operator()(int x) { return x+d; }
};

void test_pipeline()
{
ppl::stage(&inc);
ppl::stage(boost::function<int (int)>(boost::bind(&mul, 2, _1)));

boost::shared_ptr<Add> add(new Add(1));
ppl::stage(add);
}[/source]
Error: ‘operator()’ is not a member of ‘boost::shared_ptr<Add>’ [pointing at every line in the first StageTraits]

What sort of trickery might be needed? Googling only turned up stuff about variadic templates.
[TheUnbeliever]
Working with limited time, so apologies if I only get you halfway to a solution :-)



SFINAE only applies in limited situations; you won't trigger it by any arbitrary compilation failure, but rather only specific type-substitution failures. To make it work, you need some helper typedefs and some other evil.

This page is my personal go-to reference for how to get back into the mindset of C++ template metaprogramming (I'll admit to being rusty at this exact moment) and it might offer some insight into what needs to be done to make your idea work.



My hunch at the moment is that what you need is to make your first StageTraits be a special case, i.e. it needs to have some enable-if magic to prevent it from being instantiated at all for a T that isn't a functor. The easy, simple way to do this is to have your functors expose a typedef that only they hold, which is a form of tagging (I use this a lot). There might be a magic solution that can detect arbitrary functors and distinguish them from other things that look like function calls, but that's voodoo beyond my depth I'm afraid :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

No problem, that gives me a starting point. Thanks very much!
[TheUnbeliever]

This topic is closed to new replies.

Advertisement