4 Replies - 2587 Views - Last Post: 19 February 2012 - 12:08 PM

#1 ishkabible  Icon User is online

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

C++ template magic!

Posted 17 February 2012 - 07:23 PM

so after NickDMax posted the link about GoingNative 2012 I watched several of the videos. 1 of those was Stephan T. Lavavej's vid from the first day. He talked about something called "SFINAE". SFINAE states that if a function's prototype will not compile in a template then that function is not a valid contender for overload resolution. sounds innocent enough, right?

well, I've also been reading Modern C++ Design so I'm on the look out for tricky template stuff. I was able to come up with a way to check if a type has a member function using SFINAE! this has some interesting usages; you can check to see if a container is STL compliant so that a generic algorithm(like in STL algorithms) could simply accept a container rather than a pair of iterators!

here's the code I have(it even works in C++98!)

#include <iostream>
#include <vector>

template<class C>
class is_iter_container {
private:
	//not really sure what to call this.
	//it can be copy constructed from type T(user defined conversion).
	//second template argument is just to see if
	//there is a member function which returns T::iterator
	//and accepts no argumnets
	template<class T, typename T::iterator (T::*)()>
	struct bar {
		bar(const T&) {}
	};
	//this function is called if there is a member
	//T::iterator T::begin();
	//diffrent size values are returned depending on the overload chosen
	//this allows for a compile time expresion to be genrated
	template<class T>
	static char has_begin(bar<T, &T::begin>) {
		return '\0';
	}
	//this is called in all other situations but is worse becuase of ...
	//non-templates are automaticly preferd so this has be a template
	template<typename T>
	static double has_begin(...) {
		return 0.0;
	}
	template<class T>
	static char has_end(bar<T, &T::end>) {
		return '\0';
	}
	template<typename T>
	static double has_end(...) {
		return 0.0;
	}
public:
	//true if C has members..
	//		C::iterator C::begin();
	//and	C::iterator C::end();
	//note: this also checks for C::iteraotr typedef
	static const bool value =
		sizeof(has_begin<C>(C())) == sizeof(char) &&
		sizeof(has_end<C>(C())) == sizeof(char);
};

int main() {
	std::cout << is_iter_container<int>::value << '\n';
	std::cout << is_iter_container<std::vector<int> >::value << '\n';
    return 0;
}



so what magic is happening here to allow this? well the has_begin and has_end functions have 2 forms; 1 which accepts a convertible(becuase of the copy constructor) value(bar<T, ....>) and another which accepts anything. the first is disregarded if the prototype can't compile and the only way it can compile is if it has the member we want. the second function accepts anything but the ... is the least specialized overload possible so it only gets called when the first can't compile.

C++ never ceases to amaze me :wub:

So what template magic have you discovered, found, or read about?

edit:
fixed SFINAE spelling.

This post has been edited by ishkabible: 18 February 2012 - 10:18 AM


Is This A Good Question/Topic? 0
  • +

Replies To: C++ template magic!

#2 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 651
  • View blog
  • Posts: 2,231
  • Joined: 31-December 10

Re: C++ template magic!

Posted 18 February 2012 - 08:04 AM

It's actually "SFINAE" and it stands for "substitution failure is not an error".
Was This Post Helpful? 0
  • +
  • -

#3 ishkabible  Icon User is online

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: C++ template magic!

Posted 18 February 2012 - 10:29 AM

opps, fixed it! thank you :)

I ran into something kinda strange when attempting to use this. It appears that you can't use template argument deduction when using a prototype like the template<typename T> void sort(typename enable_if<true, T>::value& c).

#include <iostream>
#include <vector>
#include <algorithm>

template<bool x, class T>
struct enable_if {};

template<class T>
struct enable_if<true, T> {
	typedef T value;
};

template<class T>
struct enable_if<false, T> {};

template<typename T>
void sort(typename enable_if<true, T>::value& c) {
	std::sort(c.begin(), c.end());
}

int main() {
    std::vector<int> x;
    sort(x); //error! is this supposed to happen?s
    sort<std::vector<int> >(x); //works...
    return 0;
}


I still can't figure if this is the way it's supposed to be or not :/

This post has been edited by ishkabible: 18 February 2012 - 10:30 AM

Was This Post Helpful? 0
  • +
  • -

#4 Karel-Lodewijk  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 449
  • View blog
  • Posts: 849
  • Joined: 17-March 11

Re: C++ template magic!

Posted 19 February 2012 - 07:40 AM

Concepts were supposed to make SFINAE a thing of the past. But since this feature missed the C++11 deadline I guess it won't be in C++ for another 10 years or so. SFINAE does allow you to resolve any template overload any way you want to but the interface (even when using boost::enable_if/std::enable_if) is horrible at best.

@ishkabible

To answer your question, the only way automatic template parameter deduction works is if you use a template parameter directly, not if you accept a nested type, not if you use it in some template magic to get some other type. Not if you use SFINAE directly on it.

You can however put the SFINAE (in the form of the enable_if macro) with any parameter you like or the return type. So use it on a non-dependent parameter or better yet the return type as it takes no part in template resolution anyway and is usually less weird.

So:

template<typename T>
typename enable_if<true, void>::value sort(T& c) {
	std::sort(c.begin(), c.end());
}



In your condition it is still allowed to use the dependent type T.

Also, use boost::enable_if or c++11's std::enable if. The only thing worse than SFINAE is people trying to run their own SFINAE.

This post has been edited by Karel-Lodewijk: 19 February 2012 - 07:44 AM

Was This Post Helpful? 1
  • +
  • -

#5 ishkabible  Icon User is online

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: C++ template magic!

Posted 19 February 2012 - 12:08 PM

yep that worked!

template<typename T>
typename std::enable_if<is_iter_container<T>::value, void>::type
sort(T& c) {
	std::cout<<"hey it called this!!\n";
	std::sort(c.begin(), c.end());
}


Quote

Concepts were supposed to make SFINAE a thing of the past. But since this feature missed the C++11 deadline I guess it won't be in C++ for another 10 years or so. SFINAE does allow you to resolve any template overload any way you want to but the interface (even when using boost::enable_if/std::enable_if) is horrible at best.


ya, I was really looking forward to it to. Bjarne said they *might* get 1 big feature in within the next 3-5 years(C++1y I believe it's being called) along with numerous smaller features and library/trXs additions. I either want concepts or static if; concepts have concept_maps but static if allows numbers to be used. I wonder what the issue with using numbers as concept arguments is; that would be the best of both worlds if you ask me.

modules are the other possible item it seems. I'm not sure how much I like the idea; I haven't seen very many clear cut examples :/

I actually want to see multiple dispatch; there was a proposal called "open multi methods" which allowed you to create functions that worked just like a compile time overload selection but at run-time. it would have the same overhead as virtual functions already do so the adage "you pay for what you use" wont be violated yet you get more flexibility.

This post has been edited by ishkabible: 19 February 2012 - 12:47 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1