C++ Wrangling.
Nov. 29th, 2008 12:33 pmMy head hurts because I have been doing C++ metaprogramming for the last 3 days. I've been figuring out the actual rules relating to type deductions for template functions, and how they interact with SFINAE (Substitution Failure Is Not An Error -- except, of course, when it is).
Basically, what I am trying to do is create a geometric math library, called GUS, with a bunch of high end features that I've yet to see in any similar libraries. My first objective was to just have a definition of a vector template that took the right number of arguments. So that one could say:
gus::vector<int,3> v(1,3,6);
Thus, I needed a constructor that would take a number of parameters equal to the value passed into the template. My initial plan was to define my constructor as a member template and use type deduction to count the number of arguments passed in, and do reasonableness checking based on that.
I dug around and found out how type deduction for template functions works, and it turns out that there is a list of the allowable deductions, which are applied recursively to the argument types passed into a template function, and which are used to deconstruct the arguments for type (and other) information:
Thanks to an IM chat yesterday with
pphaneuf, I was put on the right path to getting my library written. It turns out that template members are only instantiated when called, so I could use macros to create constructors that took 0,1,2,3,... arguments and applied them in turn. Only the constructor with the right number of arguments would compile, but that wouldn't be a problem so long as no one ever called a constructor with the wrong number of arguments.
Given what he told me, I was able to get my stub library to compile and run correctly, but the results failed in the user friendly category. When one made a mistake in calling the library the C++ error messages were both obscure and unhelpful. Since this is a common problem with C++ error messages, it wasn't clear I could improve things, but I decided to give it a try.
Thats where SFINAE came in. If I created a bunch of member template constructors, and had all of them (except one) fail to substitute correctly, then rather than some obscure message about the inner guts of my library, a bad constructor call would produce an error saying something like 'wrong number of arguments for constructor', which would be much friendlier.
So, my first few (dozen) attempts all failed, because it turns out that the rules for when SFINAE works, and when it generates an error were not clear. I finally figured out, that for a working SFINAE failure in function templates:
There are still some issues in my stripped-down prototype that I need to work on, but I'm now confident that progress can start to be made at some sort of reasonable speed.
Basically, what I am trying to do is create a geometric math library, called GUS, with a bunch of high end features that I've yet to see in any similar libraries. My first objective was to just have a definition of a vector template that took the right number of arguments. So that one could say:
gus::vector<int,3> v(1,3,6);
Thus, I needed a constructor that would take a number of parameters equal to the value passed into the template. My initial plan was to define my constructor as a member template and use type deduction to count the number of arguments passed in, and do reasonableness checking based on that.
I dug around and found out how type deduction for template functions works, and it turns out that there is a list of the allowable deductions, which are applied recursively to the argument types passed into a template function, and which are used to deconstruct the arguments for type (and other) information:
More info is here. In any case, you'll note that there is absolutely no way to count arguments, so that idea was a bust. In fact its a bust in another way as well, because I hadn't realized that in a template function the number of arguments is part of the template signature, so its not possible to define a given template function that takes a differing number of arguments (without using '...' that is). So, I gave up on that approach.T const T volatile T T& T* T[9] A<T> C(*)(T) T(*)() T(*)(U) T C::* C T::* T U::* T (C::*)() C (T::*)() D (C::*)(T) C (T::*)(U) T (C::*)(U) T (U::*)() T (U::*)(V) E[9][i] B<i> TT<T> TT<i> TT<C>
- T, U, and V represent a template type argument
- 9 represents any integer constant
- i represents a template non-type argument
- [i] represents an array bound of a reference or pointer type, or a non-major array bound of a normal array.
- TT represents a template template argument
- (T), (U), and (V) represents an argument list that has at least one template type argument
- () represents an argument list that has no template arguments
- <T> represents a template argument list that has at least one template type argument
- <i> represents a template argument list that has at least one template non-type argument
- <C> represents a template argument list that has no template arguments dependent on a template parameter
Thanks to an IM chat yesterday with
Given what he told me, I was able to get my stub library to compile and run correctly, but the results failed in the user friendly category. When one made a mistake in calling the library the C++ error messages were both obscure and unhelpful. Since this is a common problem with C++ error messages, it wasn't clear I could improve things, but I decided to give it a try.
Thats where SFINAE came in. If I created a bunch of member template constructors, and had all of them (except one) fail to substitute correctly, then rather than some obscure message about the inner guts of my library, a bad constructor call would produce an error saying something like 'wrong number of arguments for constructor', which would be much friendlier.
So, my first few (dozen) attempts all failed, because it turns out that the rules for when SFINAE works, and when it generates an error were not clear. I finally figured out, that for a working SFINAE failure in function templates:
- The substitution failure must happen before the body of the function. That is, something has to fail in the type signature of the template function.
- The thing that fails must be dependant on the template parameter, or the compiler is allowed to decide its a static error.
- If you're using type deduction, when the thing that fails, fails to fail (as it were) it must do so in a way that allows for the type deduction to work.
There are still some issues in my stripped-down prototype that I need to work on, but I'm now confident that progress can start to be made at some sort of reasonable speed.
no subject
Date: 2008-11-29 07:53 pm (UTC)