C++ template floating point constant parameter
Started by kulik, May 29 2006 04:08 PM
14 replies to this topic
#1
Posted 29 May 2006 - 04:08 PM
I wanted to code a measurement unit conversion code to aid me to convert radians to degrees, metres to kilometres, etc.
I didn't want to polute the code with macros or such so I tried to implement it using metaprogramming.
I defined some basic template classes like BaseUnit and DerivedUnit.
Both take HeldType typename parameter. However DerivedUnit needs some kind of ratio so it's able to convert itself to BaseUnit.
DerivedUnit has operator BaseUnit() so it can be converted to it.
Both BaseUnit and DerivedUnit can be explicitly set to their HeldTypes (floats, doubles, whatever)
You know, then could just:
typedef BaseUnit<double> Meter;
typedef DerivedUnit<Meter, 1000> Kilometer;
// this is just for simplicity sake, you would have to inherit your units to avoid implicit conversions between various BaseUnits of same HeldType
void something( const Meter& v )
{
// do something
}
int main()
{
doSomething( Kilometer( 2.0 ) );
// the operator Meter() in Kilometer (auto instantiated using templates would convert it to Meter with no problems).
}
This all would work as long as I won't need any floating point ratio (metres, kilometres, megametres, whatever just use unsigned int ratios). But in fact I need floating point ratios very much (radians vs degrees most notably).
I want to do things like:
typedef DerivedUnit<Radian, 180.0 / PI> Degree;
However ISO C++ deprecated floating point template arguments I don't know why, I searched internet, mailgroups. It doesn't seem to be very old change, and noone is missing the feature. There must be some more elegant workaround.
First I though about nominator, denominator solution, however that seems very nasty and unelegant.
EDIT: Forgot to say, my target is gcc 4.1, no need to care about Visual Studio 6.0 or so. It would be great if it worked in newest Visual Studio and if it was ISO C++ compliant.
I didn't want to polute the code with macros or such so I tried to implement it using metaprogramming.
I defined some basic template classes like BaseUnit and DerivedUnit.
Both take HeldType typename parameter. However DerivedUnit needs some kind of ratio so it's able to convert itself to BaseUnit.
DerivedUnit has operator BaseUnit() so it can be converted to it.
Both BaseUnit and DerivedUnit can be explicitly set to their HeldTypes (floats, doubles, whatever)
You know, then could just:
typedef BaseUnit<double> Meter;
typedef DerivedUnit<Meter, 1000> Kilometer;
// this is just for simplicity sake, you would have to inherit your units to avoid implicit conversions between various BaseUnits of same HeldType
void something( const Meter& v )
{
// do something
}
int main()
{
doSomething( Kilometer( 2.0 ) );
// the operator Meter() in Kilometer (auto instantiated using templates would convert it to Meter with no problems).
}
This all would work as long as I won't need any floating point ratio (metres, kilometres, megametres, whatever just use unsigned int ratios). But in fact I need floating point ratios very much (radians vs degrees most notably).
I want to do things like:
typedef DerivedUnit<Radian, 180.0 / PI> Degree;
However ISO C++ deprecated floating point template arguments I don't know why, I searched internet, mailgroups. It doesn't seem to be very old change, and noone is missing the feature. There must be some more elegant workaround.
First I though about nominator, denominator solution, however that seems very nasty and unelegant.
EDIT: Forgot to say, my target is gcc 4.1, no need to care about Visual Studio 6.0 or so. It would be great if it worked in newest Visual Studio and if it was ISO C++ compliant.
#2
Posted 29 May 2006 - 08:18 PM
Quote
However ISO C++ deprecated floating point template arguments I don't know why
A fix that fits in your design would be to make the template parameter a float reference or a function returning a float.
const double RadDegFactor = 180.0 / PI; typedef DerivedUnit<Radian, RadDegFactor> Degree;
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#3
Posted 29 May 2006 - 08:30 PM
Thanks a million, that works.
But does that mean, that the constant floating point ratio won't be compile-time inlined. I mean I only pass reference, so even with -O3 it won't get into the method's body (not a big deal though).
Thanks, yes it wasn't deprecated by ISO C++ (it never existed there) but by gcc - my mistake.
But does that mean, that the constant floating point ratio won't be compile-time inlined. I mean I only pass reference, so even with -O3 it won't get into the method's body (not a big deal though).
Thanks, yes it wasn't deprecated by ISO C++ (it never existed there) but by gcc - my mistake.
#4
Posted 29 May 2006 - 08:57 PM
Depends on the compiler really, but a reference to a const float means the compiler can optimize it as it knows it's value and that it should never change. The same goes for a function btw, if you make it inline it can see that it'll always return the same value and so it will use that value directly in the code.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#5
Posted 30 May 2006 - 12:55 PM
I also really miss that lost functionality of g++, snif.
I understand it could have caused much grief if you got caught with your pants down, and it wasn't portable at all, but emulating it is a pain.
So until floats get promoted to first class citizen (/me keeps fingers crossed), depending on exactly what you're trying to acheive you have to kludge around; that generaly means using 32/64bit integers and dirty union tricks. Cumbersome, ugly but portable.
I understand it could have caused much grief if you got caught with your pants down, and it wasn't portable at all, but emulating it is a pain.
So until floats get promoted to first class citizen (/me keeps fingers crossed), depending on exactly what you're trying to acheive you have to kludge around; that generaly means using 32/64bit integers and dirty union tricks. Cumbersome, ugly but portable.
#6
Posted 30 May 2006 - 07:30 PM
how about two integers to define the ratio?
like.. typedef DerivedUnit< Meter, 1,1000 > MilliMeter; // 1,1000 == 1/1000
like.. typedef DerivedUnit< Meter, 1,1000 > MilliMeter; // 1,1000 == 1/1000
davepermen.net
-Loving a Person is having the wish to see this Person happy, no matter what that means to yourself.
-No matter what it means to myself....
-Loving a Person is having the wish to see this Person happy, no matter what that means to yourself.
-No matter what it means to myself....
#8
Posted 30 May 2006 - 09:41 PM
typedef DerivedUnit<Meter, 201168, 1000> Furlong;
typedef DerivedUnit<Meter, 0x6DA0192FEAB900, 1> Parsec;
64bit are enough to cover the solar system with sub-micron precision - more than 3 bits left at that scale - :ninja:
typedef DerivedUnit<Meter, 0x6DA0192FEAB900, 1> Parsec;
64bit are enough to cover the solar system with sub-micron precision - more than 3 bits left at that scale - :ninja:
#9
Posted 31 May 2006 - 03:01 AM
Insomnia ftw.
I've just tried those again and even if i'm sure i did, apparently, i'm full of it I can't escape the integral non-type argument rule because i can't generate strictly compile time integrals out of floats anymore. :whistle:
Like .oisyn pointed out, modern compilers are smarter than you think, gcc included.
http://forum.hardwar...-2.htm#t1344485
Sorry for the french spammage - out of lazyness- but you shouldn't need any translation to grok where i was going in that post.
tbp said:
depending on exactly what you're trying to acheive you have to kludge around; that generaly means using 32/64bit integers and dirty union tricks. Cumbersome, ugly but portable.
kulik said:
But does that mean, that the constant floating point ratio won't be compile-time inlined. I mean I only pass reference, so even with -O3 it won't get into the method's body (not a big deal though).
http://forum.hardwar...-2.htm#t1344485
Sorry for the french spammage - out of lazyness- but you shouldn't need any translation to grok where i was going in that post.
#10
Posted 31 May 2006 - 05:42 AM
Hmm, instead of a template<float> class, maybe you could get some mileage out of a singleton factory class with a float constructor argument? Something like:
UnitFactory<Meter> KilometerFactory(1000.0f);
Meter threeKlicks = KilometerFactory.Get(3.0f); // 3 kilometers in Meter unit
UnitFactory<Meter> KilometerFactory(1000.0f);
Meter threeKlicks = KilometerFactory.Get(3.0f); // 3 kilometers in Meter unit
#11
Posted 31 May 2006 - 11:40 AM
This would have the added advantage that you could use the conversion routines with variables which is not possible with metaprograms.
#12
Posted 31 May 2006 - 12:31 PM
It is with functions or functors.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#13
Posted 31 May 2006 - 02:01 PM
davepermen said:
how about two integers to define the ratio?
like.. typedef DerivedUnit< Meter, 1,1000 > MilliMeter; // 1,1000 == 1/1000
like.. typedef DerivedUnit< Meter, 1,1000 > MilliMeter; // 1,1000 == 1/1000
kulik said:
First I though about nominator, denominator solution, however that seems very nasty and unelegant.
nominator / denominator = ratio
I will probably go that way
btw: Singleton factory isn't a bad idea but i want to do this
example:
void Object::setRotation( const Radian& rot );
if you call this with whatever unit it will get automatically converted to Radian. Singleton would be great to parse config files or such, but this could be archived with metaprogramming I think.
thanks for all suggestions
#14
Posted 31 May 2006 - 05:03 PM
What happens when you define your template like this?
It works just fine in VC8, but I'm a little confused about where the RATIO is stored. :wacko:
template<typename BASE, const double & RATIO>
class DerivedUnit
{
...
};
typedef UnitBase<float> Meter;
typedef DerivedUnit<Meter, 1000.0> Kilometer;
It works just fine in VC8, but I'm a little confused about where the RATIO is stored. :wacko:
#15
Posted 31 May 2006 - 05:52 PM
That actually works in VC8? 
It certainly isn't ISO C++. Furthermore, each translation unit would probably have it's own Kilometer type.
It certainly isn't ISO C++. Furthermore, each translation unit would probably have it's own Kilometer type.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
1 user(s) are reading this topic
0 members, 1 guests, 0 anonymous users












