4

The following example might seem nonsensical, but it's part of a larger high-performance code where the presented technique makes sense. I mention this just in case someone should suspect an XY question - it's most probably not.

I have a function with templated/compile-time operand:

template <int M> int mul(int x){ return M * x; } 

Now I want to do the same for double, what is - of course - not allowed:

template <double M> // you can't do that! int mul(double x){ return M * x; } 

So to still put in the double at compile time, I only see the following solution:

// create my constants struct SevenPointFive{ static constexpr double VAL = 7.5; } struct ThreePointOne{ static constexpr double VAL = 3.1; } // modified function template <class M> int mul(double x){ return M::VAL * x; } // call it double a = mul<SevenPointFive>(3.2); double b = mul<ThreePointOne>(a); 

Is there a better solution for the problem to somehow pass a double constant in a template parameter, without creating a struct for each value?

(I'm interested in a solution which actually uses double/float, not a hack with using two ints to create a rational number or fixed point ideas such as y = 0.01 * M * x.)

8
  • 3
    You do not need templates here, just overloaded functions. Commented Jul 4, 2015 at 10:11
  • 4
    @Michael various hacks come to mind; not sure if good or not - you could template base on <size_t nom, size_t denom> for rational numbers, e.g.. Commented Jul 4, 2015 at 10:14
  • Quick & dirty workaround: template <int M> int mul(int x){ return (0.1 * M) * x; }, instantiate with 75 and 31. Commented Jul 4, 2015 at 10:15
  • @DieterLücking: in what way can overloading replace templates and avoid code duplication ?? Commented Jul 4, 2015 at 10:24
  • @AmiTavory and Yves Daous: Thank's for your answers. These might be reasonable in many/most situations, but I'd be interested in a real floating point solution here. Commented Jul 4, 2015 at 10:32

4 Answers 4

2

In C++11, it is not necessary to use templates at all. Simply use constexpr (generalised constant expressions) in a different way than you are.

 #include <iostream> constexpr double mul(double x, double y) { return x*y; } int main() { std::cout << mul(2.3, 3.4) << '\n'; double x; std::cin >> x; // to demonstrate constexpr works with variables std::cout << mul(2.3, x) << '\n'; } 

Although I say templates aren't necessary (which they aren't in the example given) these can be templated if needed

 template <class T> constexpr T mul(T x, T y) {return x*y;} 

or (if you want to use the function for types that are better passed by const reference)

 template <class T> constexpr T mul(const T &x, const T &y) {return x*y;} 
Sign up to request clarification or add additional context in comments.

3 Comments

"to demonstrate constexpr works with variables" - I didn't know this!
May be I was not clear enough about this, but the point of the question is on how to put a double constant into a template parameter.
Yeah, I got that. But your explanation suggested the reason is that you want compile-time evaluation. Which is what the constexpr approach achieves when the arguments values are known at compile time. My response was about what (you say) you are trying to achieve. The only difference between mul<2.3>(3.4) (if that was allowed) and mul(2.3, 3.4) with constexpr is syntactic at the point of use - not functional.
2

You can conveniently pass floating point values in template parameters using user-defined literals.

Just write a literal that creates your envelope class. Then you can write something like

mul<decltype(3.7_c)>(7) 

Or even better, have your function take the argument by value so you can write

mul(3.7_c, 7) 

the compiler will make that just as efficient.

Below's an example of code that does this:

#include <iostream> template <int Value, char...> struct ParseNumeratorImpl { static constexpr int value = Value; }; template <int Value, char First, char... Rest> struct ParseNumeratorImpl<Value, First, Rest...> { static constexpr int value = (First == '.') ? ParseNumeratorImpl<Value, Rest...>::value : ParseNumeratorImpl<10 * Value + (First - '0'), Rest...>::value; }; template <char... Chars> struct ParseNumerator { static constexpr int value = ParseNumeratorImpl<0, Chars...>::value; }; template <int Value, bool, char...> struct ParseDenominatorImpl { static constexpr int value = Value; }; template <int Value, bool RightOfDecimalPoint, char First, char... Rest> struct ParseDenominatorImpl<Value, RightOfDecimalPoint, First, Rest...> { static constexpr int value = (First == '.' && sizeof...(Rest) > 0) ? ParseDenominatorImpl<1, true, Rest...>::value : RightOfDecimalPoint ? ParseDenominatorImpl<Value * 10, true, Rest...>::value : ParseDenominatorImpl<1, false, Rest...>::value; }; template <char... Chars> using ParseDenominator = ParseDenominatorImpl<1, false, Chars...>; template <int Num, int Denom> struct FloatingPointNumber { static constexpr float float_value = static_cast<float>(Num) / static_cast<float>(Denom); static constexpr double double_value = static_cast<double>(Num) / static_cast<double>(Denom); constexpr operator double() { return double_value; } }; template <int Num, int Denom> FloatingPointNumber<-Num, Denom> operator-(FloatingPointNumber<Num, Denom>) { return {}; } template <char... Chars> constexpr auto operator"" _c() { return FloatingPointNumber<ParseNumerator<Chars...>::value, ParseDenominator<Chars...>::value>{}; } template <class Val> int mul(double x) { return Val::double_value * x; } template <class Val> int mul(Val v, double x) { return v * x; } int main() { std::cout << mul<decltype(3.79_c)>(77) << "\n"; std::cout << mul(3.79_c, 77) << "\n"; return 0; } 

1 Comment

BTW to make it a complete solution, you'd also need to handle parsing scientific notation (e.g. 7.5e5_c); but I'll leave that out to keep the example simple.
1
constexpr double make_double( int64_t v, int64_t man ); 

write a function that makes a double from a base and mantissa. This can represent every non-special double.

Then write:

template<int64_t v, int64_t man> struct double_constant; 

using the above make_double and various constexpr access methods.

You can even write base and exponent extracting constexpr functions I suspect. Add a macro to remove DRY, or use a variable.


Another approach is:

const double pi=3.14;//... template<double const* v> struct dval{ operator double()const{return *v;} }; template<class X> double mul(double d){ return d*X{}; } double(*f)(double)=mul<dval<&pi>>; 

which requires a variable to point to, but is less obtuse.

Comments

1

If you don't want to create type envelopes for each double/float constant used, then you can create a mapping between integer and double constants. Such mapping can be implemented e.g. as follows:

#include <string> #include <sstream> template<int index> double getValue() { std::stringstream ss("Not implemented for index "); ss << index; throw std::exception(ss.str()); } template<> double getValue<0>() { return 3.6; } template<> double getValue<1>() { return 7.77; } template<int index> double multiply(double x) { return getValue<index>() * x; } 

Alternative options to implement the mapping are via a function which does switch-case on the input integer parameter and returns a float/double, or indexing to an array of constants, but both these alternatives would require constexpr for them to happen on compile-time, while some compilers still do not support constexpr: constexpr not compiling in VC2013

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.