TIGSource Forums

Developer => Technical => Topic started by: Kekskiller on April 18, 2010, 11:53:28 AM



Title: Cheating template specialization
Post by: Kekskiller on April 18, 2010, 11:53:28 AM
I'm redesigning all my color/vector/rect template classes and wondered how to cope with class/type-specific zero values, C functions etc. What I do have at the moment is a set of very similar classes (a Int3D and Float3D class for position and vector calculations). What I want to do now is to create a single template for both having the same methods. Problem is there are some things which bother me like when using integer zeros as initial value for all possible templates. When using a float as template type this could (and would I guess) cause a cast from integer's 0 to float's 0.0 which isn't really elegant nor good for performance. Correct my if I'm wrong. Second problem is when using functions from <math.h> and such (which are only for double or float).

The second can be solved by carefully adding casts to the function call. So my only problem at the moment is how remove the 0/0.0 conversion. I thought about using specialized template function - one for integer/non-float types and a specialization for floats. Using these as specific value-returning initializers would work great if it weren't impossible for GCC to use it.

So... any ideas to work around? A cheaty template trick or a better, cleaner idea?


Title: Re: Cheating template specialization
Post by: Average Software on April 18, 2010, 12:41:42 PM
For any built-in data type, the "default constructor" form gives you the default value.  So given a template like so:

Code:
template <typename Type>
void Funk()
{
    Type val = Type(); // Initialize to default value.
}

This will solve your default value problem.

As for the other problem, try using the C++ header <cmath> instead of the C header <math.h>.  I think <cmath> has some additional overloads, but I may be wrong.


Title: Re: Cheating template specialization
Post by: BorisTheBrave on April 18, 2010, 02:26:13 PM
The above solution is best, but I'd bet even initializing both with "0" would optimize to similar code.


Title: Re: Cheating template specialization
Post by: Average Software on April 18, 2010, 04:04:13 PM
The above solution is best, but I'd bet even initializing both with "0" would optimize to similar code.

That doesn't work for types that don't accept 0 as an initializer (most user-defined types, I reckon).


Title: Re: Cheating template specialization
Post by: Will Vale on April 18, 2010, 04:17:17 PM
Using enums is a handy way to invoke specific constructors, good for things like matrices which have additive and multiplicative identities. Plus you might want to leave the default constructor for simple value types empty for performance reasons.

Code:
enum ZeroTag { ZERO };
enum OneTag { ONE };

template <typename T> class Matrix
{
    Matrix( ZeroTag ) /* init NxN zeroes */ {}
    Matrix( OneTag ) /* init NxN identity */ {}
};

Matrix zero(ZERO);
Matrix one(ONE);

On your other point, I think this is perhaps a sign that what you're trying to do might not be a good idea. I'd be wary of casting back and forth between floats in the integer version since you could get precision errors, which should never happen with integers.

To be honest I think you might be better off keeping them as separate types. If you've already implemented them, what's the gain in joining them together, especially given that all the operations which are relevant for a float vector might not be relevant for an int vector?

Cheers,

Will



Title: Re: Cheating template specialization
Post by: TobiasW on April 18, 2010, 04:41:46 PM
How about a non-member template function returning the zero value/object? You can reuse it among various classes and templates and it is very easy to write new specializations for it without needing to change the template class(es) where it is used.


Title: Re: Cheating template specialization
Post by: Average Software on April 18, 2010, 04:57:53 PM
Using enums is a handy way to invoke specific constructors, good for things like matrices which have additive and multiplicative identities. Plus you might want to leave the default constructor for simple value types empty for performance reasons.

Code:
enum ZeroTag { ZERO };
enum OneTag { ONE };

template <typename T> class Matrix
{
    Matrix( ZeroTag ) /* init NxN zeroes */ {}
    Matrix( OneTag ) /* init NxN identity */ {}
};

Matrix zero(ZERO);
Matrix one(ONE);

C++11 (I think that's the official name now) is adding the ability to create instance of objects that are treated as compile-time constants, that is all of their data is known and can be optimized at compile time, in assignment for example.  I don't remember the syntax (it has something to do with the new keyword constexpr) but I would think it would render some of those techniques obsolete.


Title: Re: Cheating template specialization
Post by: Rusky on April 18, 2010, 05:31:51 PM
When initializing something with 0, it just means that type's default value. There will be absolutely no runtime cost when initializing a float this way. The Type() method works as well- there shouldn't be any speed problems there.

The <cmath> functions are defined for floating-point types only, but it doesn't really make sense to use them with integers anyway. They will almost always return non-integer values. You generally want to use floating point in situations where you want to use those functions.


Title: Re: Cheating template specialization
Post by: Average Software on April 18, 2010, 05:33:58 PM
When initializing something with 0, it just means that type's default value.

Not for user-defined types.  Type() works for both user-defined and built-in types, 0 does not.


Title: Re: Cheating template specialization
Post by: Will Vale on April 18, 2010, 06:22:50 PM
When initializing something with 0, it just means that type's default value.

When constructing an instance with zero, it means roughly "cast the integer zero to something a constructor for this type accepts, if one exists, and store this value in the instance". If no suitable constructor is available, you'll get a compile time error.

Quote
There will be absolutely no runtime cost when initializing a float this way.

<pedantic>There is always a (very small) runtime cost to *store* the value. The cast is usually free, assuming it's not a cast to some complex user type.</pedantic>

Quote
The Type() method works as well

If you use Type(), you get a default-constructed instance, which is not the same thing. Specifically, the value of a default-constructed built-in type is undefined. For a user-created typed it'll depend on what the types's default constructor does. If no default constructor is available (e.g. if it's absent or private) you'll get a compile time error.

Not initialising things is a common cause of "it runs OK in debug" bugs - many debug runtime libraries backfill memory with a pattern like 0xCD or 0xFF, whereas the release versions don't so you get whatever was there before - often zero.

HTH,

Will



Title: Re: Cheating template specialization
Post by: Kekskiller on April 18, 2010, 10:38:37 PM
For any built-in data type, the "default constructor" form gives you the default value.  So given a template like so:

Code:
template <typename Type>
void Funk()
{
    Type val = Type(); // Initialize to default value.
}

This. Perfect answer. Tried it, works great! Didn't know it works with buildin ones, too. And I finally understood whats behind the int(...) cast - it's like creating a new object out of the original value, except that it's casted.

This will solve your default value problem.

As for the other problem, try using the C++ header <cmath> instead of the C header <math.h>.  I think <cmath> has some additional overloads, but I may be wrong.

Nope, at a maximum only floats and doubles for both libraries. Or atleast for the C++ variant.

On your other point, I think this is perhaps a sign that what you're trying to do might not be a good idea. I'd be wary of casting back and forth between floats in the integer version since you could get precision errors, which should never happen with integers.

 If you've already implemented them, what's the gain in joining them together, especially given that all the operations which are relevant for a float vector might not be relevant for an int vector?

I only use float and int for coordinates. And convert between them to get a) less details due to conversion or b) more details due to later operations. There is no case where I loose wanted details (otherwise it wouldn't the great way it works atm ;) ). See, I have a grid in my game and every object has an int value (for rastering and faster access) and a float versions (for physics). I only the float versions for physics with no unwanted loss when converting back to int (dropping the float details is what I want at this point since each int has).

And NO, I won't discuss whether it's good or not to store grid/physics seperation like that.

More specific, this is (was!) my problem:

Code:
int Distance2D() {return int(sqrt(pow(float(x),2.0)+pow(float(y),2.0)));}

It's a method of my 3D vector class (x,y,z...), the "Int3D" variant. My idea is to convert into something like different, like:

Code:
T Distance2D() {return T(sqrt(pow(float(x),2.0)+pow(float(y),2.0)));}

or

Code:
T Distance2D() {return T(std::sqrt(pow(float(x),2.0)+pow(float(y),2.0)));}

In this way I would a) guerantee that every possible int gets in the correct float format and every resulting value will be converted back to it's right place. Before rewriting the code I only used the int version, which was my only concern. If I want to calculate the distance using integer I always have to use floats using this C method! Therefore, if I wanted a float distance for integers I would just convert them to float and then use the method. Otherwise I can make use of the int variant which won't be a problem with my program. I also guess the float(float) cast WILL be optimized by the compiler since it's obviously (and logically) superfluous.

To be honest I think you might be better off keeping them as separate types.

I kept them as seperate types before and it was more than annoying to maintain all them when adding a new feature relevant to all of them. They are so similar and I'v never used calculations which won't work for both int and float. It's the way to go. There is no reason to not using one template for this project, a birectional thing.


Title: Re: Cheating template specialization
Post by: Average Software on April 18, 2010, 11:13:55 PM
For any built-in data type, the "default constructor" form gives you the default value.  So given a template like so:

Code:
template <typename Type>
void Funk()
{
    Type val = Type(); // Initialize to default value.
}

This. Perfect answer. Tried it, works great! Didn't know it works with buildin ones, too. And I finally understood whats behind the int(...) cast - it's like creating a new object out of the original value, except that it's casted.

It isn't a cast, it's the creation of a temporary via a constructor.

Code:
int(4.0); // Temporary int from value 4.0.
(int)4.0; // C-style cast of the value 4.0.

For the built-in types, there is no difference between the two that I'm aware of, but for user-defined types they can have very different behavior (conversion operators come into play) and it's important to know the difference.


Title: Re: Cheating template specialization
Post by: Kekskiller on April 18, 2010, 11:26:11 PM
It isn't a cast, it's the creation of a temporary via a constructor.

Aww, come one you nitpicker! You'll atleast need a cast to convert a value from one to another. May it be within the constructor or outside of it.

* Kekskiller casts an integer flameball on Average Software's nitpicking unit


Title: Re: Cheating template specialization
Post by: Kekskiller on April 19, 2010, 04:11:57 AM
For everyone who's interested: My final solution is a a bit more complex than I thought cause I also wanted the possiblity to add floats to integers, etc.

Code:
template <class T> struct Vector3D {
  // a vector class containing type-independent calculations like *,/,+,- etc and such
  // except constructors (this class isn't supposed for direct use)
};

template <class T> struct Pos3D: public Vector3D<T> {
  // only type-independent constructors
};
template <> struct Pos3D<int>: public Vector3D<int> {
  // constructors for int AND float parameters
}
template <> struct Pos3D<float>: public Vector3D<float> {
  // constructors for float AND int parameters
};

This way I can put all normal calculations into Vector3D and all initializers/type-specific methods/constructors into the specialized templates. So far it seems the only way cover such things to me. Atleast I don't need to duplicate ALL methods/operations like when using specialization of non-inherited classes.

Hooray for templates and classes.


Title: Re: Cheating template specialization
Post by: BorisTheBrave on April 19, 2010, 12:39:11 PM
Actually, why the heck do you want integer vectors? They are useless for virtually everything, except as array indexes.


Title: Re: Cheating template specialization
Post by: Mikademus on April 19, 2010, 01:00:00 PM
Actually, why the heck do you want integer vectors? They are useless for virtually everything, except as array indexes.

One reason I can think of at the top of my head is for "typedef Vector<int, 2> ScreenCoordinate;", where you already have all mathematical operators and implicit conversions defined.


Title: Re: Cheating template specialization
Post by: BorisTheBrave on April 19, 2010, 01:49:42 PM
I guess for pixel perfect co-ordinates, but I'd still use generally floats and then cast to ints at the last minute.


Title: Re: Cheating template specialization
Post by: Kekskiller on April 19, 2010, 01:56:10 PM
I guess for pixel perfect co-ordinates, but I'd still use generally floats and then cast to ints at the last minute.

Oh, thats such a waste.


Title: Re: Cheating template specialization
Post by: Tycho Brahe on April 19, 2010, 02:05:43 PM
I guess for pixel perfect co-ordinates, but I'd still use generally floats and then cast to ints at the last minute.
I'd have thought that wouldnt be very good for memory usage? would it? they generally (for single precision float vs short int) use about double the number of bits.


Title: Re: Cheating template specialization
Post by: drChengele on April 19, 2010, 02:25:42 PM
I guess for pixel perfect co-ordinates, but I'd still use generally floats and then cast to ints at the last minute.
Not that I don't do the same when I need it, but it is worth remembering that a float to int conversion is about 10 times slower (50 instructions long compared to 4-5, I forget the exact numbers) for int to float. That may not seem like much on modern processors, but it's only a matter of time before you get the chance to use it in a time-critical section of the code which executes in exponential time or something like that.


Title: Re: Cheating template specialization
Post by: John Nesky on April 19, 2010, 02:31:57 PM
That's a valid point... but it's still an O(1) operation. :) Premature optimization and all that.


Title: Re: Cheating template specialization
Post by: muku on April 19, 2010, 03:19:11 PM
Semi-on-topic-ish remark: the standard C float-to-int cast is indeed a shockingly slow beast, which has historical reasons related to the C standard. It's very useful to know that there are ways to do this conversion about 10 times faster:

http://www.stereopsis.com/FPU.html#convert
http://mega-nerd.com/FPcast/

If you ever need to convert floats to ints in anything remotely performance sensitive, do yourself the favor and use one of those. I've seen dramatic increases in frames per seconds for certain pieces of code just by making use of these things.


Title: Re: Cheating template specialization
Post by: drChengele on April 19, 2010, 03:33:04 PM
Hey, that should come in really useful! Thanks!

[/derail]


Title: Re: Cheating template specialization
Post by: Will Vale on April 19, 2010, 03:47:22 PM
IIRC with MSVC the clue is if _ftol() shows up in your profile, you're being bitten by slow runtime library float->int conversions.

Screen coordinates for UI and sprites are a problem. You kind of want them in integers since you're talking about pixels, but (if you're using any graphics hardware) they'll have to be converted to floats to draw them eventually, so maybe it's better to store them in floats. Plus you might have a use for subpixel accuracy. I still don't have a rule of thumb I'm happy with for this :(


Title: Re: Cheating template specialization
Post by: Average Software on April 19, 2010, 03:47:44 PM
Semi-on-topic-ish remark: the standard C float-to-int cast is indeed a shockingly slow beast, which has historical reasons related to the C standard. It's very useful to know that there are ways to do this conversion about 10 times faster:

http://www.stereopsis.com/FPU.html#convert
http://mega-nerd.com/FPcast/

If you ever need to convert floats to ints in anything remotely performance sensitive, do yourself the favor and use one of those. I've seen dramatic increases in frames per seconds for certain pieces of code just by making use of these things.

Interesting.  I wonder if different architectures are better at it.  I would never make such an optimization, because I don't like to assume x86.


Title: Re: Cheating template specialization
Post by: muku on April 19, 2010, 11:33:28 PM
Interesting.  I wonder if different architectures are better at it.  I would never make such an optimization, because I don't like to assume x86.

There's also lrint and friends (http://www.opengroup.org/onlinepubs/000095399/functions/lrint.html), which should do this kind of stuff portably (it was also mentioned in the second link, I think). As far as I know it's a C99 thing, so I'm not sure how well-supported it is, and whether it always gives the same speedup. Worth a try I guess.

Even if you don't want to limit yourself to x86, you could still use the preprocessor to disable these optimizations in the unlikely case that you're not on x86.


Title: Re: Cheating template specialization
Post by: Average Software on April 20, 2010, 04:45:31 AM
Interesting.  I wonder if different architectures are better at it.  I would never make such an optimization, because I don't like to assume x86.

There's also lrint and friends (http://www.opengroup.org/onlinepubs/000095399/functions/lrint.html), which should do this kind of stuff portably (it was also mentioned in the second link, I think). As far as I know it's a C99 thing, so I'm not sure how well-supported it is, and whether it always gives the same speedup. Worth a try I guess.

Even if you don't want to limit yourself to x86, you could still use the preprocessor to disable these optimizations in the unlikely case that you're not on x86.

For me it's not unlikely, it's 100% certain, as I build Mac OS X universal binaries for PowerPC and x86.