Jump to content


C++ template floating point constant parameter


14 replies to this topic

#1 kulik

    Member

  • Members
  • PipPip
  • 41 posts

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.
The Eleventh - completely open source RPG game
Tenuity3D - cross-platform OpenGL game engine

#2 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 29 May 2006 - 08:18 PM

Quote

However ISO C++ deprecated floating point template arguments I don't know why
Deprecated? They never existed in the first place. Because integers are exact, floats aren't. A simple value like 0.4 can't be represented by a float, so that's waiting for a disaster to happen. (Maybe gcc supported it as an extension, but deprecated it later on when proven it caused problems?)

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.

#3 kulik

    Member

  • Members
  • PipPip
  • 41 posts

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.
The Eleventh - completely open source RPG game
Tenuity3D - cross-platform OpenGL game engine

#4 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

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.

#5 tbp

    Valued Member

  • Members
  • PipPipPip
  • 135 posts

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.

#6 davepermen

    Senior Member

  • Members
  • PipPipPipPip
  • 1306 posts

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
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....

#7 monjardin

    Senior Member

  • Members
  • PipPipPipPip
  • 1033 posts

Posted 30 May 2006 - 07:39 PM

That's fine for meters to millimeters, but what if he needs a length in furlongs or parsecs? ;)
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

#8 tbp

    Valued Member

  • Members
  • PipPipPip
  • 135 posts

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:

#9 tbp

    Valued Member

  • Members
  • PipPipPip
  • 135 posts

Posted 31 May 2006 - 03:01 AM

Insomnia ftw.

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.
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:


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).
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.

#10 Borogove

    New Member

  • Members
  • PipPip
  • 16 posts

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

#11 SmokingRope

    Valued Member

  • Members
  • PipPipPip
  • 210 posts

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 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 31 May 2006 - 12:31 PM

It is with functions or functors.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#13 kulik

    Member

  • Members
  • PipPip
  • 41 posts

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

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
The Eleventh - completely open source RPG game
Tenuity3D - cross-platform OpenGL game engine

#14 monjardin

    Senior Member

  • Members
  • PipPipPipPip
  • 1033 posts

Posted 31 May 2006 - 05:03 PM

What happens when you define your template like this?
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:
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

#15 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 31 May 2006 - 05:52 PM

That actually works in VC8? :blink:

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.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users