I'm writing code for scientific calculation which uses trigonometric functions, and since I need to use not only float/double but also multiprecision floating point numbers, I'm templatizing the functions and classes.
Let's say I'm simply writing a function to calculate sin(pi*x) * cos(pi*x):
template <class Real>
Real sincos(const Real& x);
How can I use proper versions of trigonometric functions and pi value? Multiprecision floating point libraries usually have their own version of trigonometric functions, and std:: versions are defined only for float, double, and long double, and M_PI is not even standard.
I tried putting function pointers as arguments, but std:: versions are overloaded functions not template, so I should put it like (double (*)(double)&std::sin) which hurts readability and it's hard to use.
template <class Real>
Real sincos(const Real& x,
Real (*sin)(const Real&), Real (*cos)(const Real&), const Real& pi)
{
return sin(pi*x) * cos(pi*x);
}
// I don't think it's well designed function if it's hard to use like this.
double s = sincos<double>(0, (double (*)(double))&std::sin,
(double (*)(double))&std::cos,
M_PI);
my_mpf = sincos<my_mpf>(0, somewhere::my_sin, somewhere::my_cos, my_mpf_version_of_pi);
The problem is that there are lots of math functions needed, so I can not simply put those into the function parameters.
How should I generalize those calculations?
You can consider going the char_traits route.
// default implementation calls std::<stuff>
template<class T>
struct trig_traits {
static constexpr T pi() { return T(3.14159265359); }
static auto sin(T v) { return std::sin(v); }
static auto cos(T v) { return std::cos(v); }
// etc.
};
You can then specialize trig_traits<my_mpf> as needed. Your actual function template will then look like
template <class Real>
Real sincos(const Real& x) {
using traits = trig_traits<Real>;
return traits::sin(traits::pi() * x) * traits::cos(traits::pi() * x);
}
My suggestion:
Provide function overloads for the functions that return PI, sin, and cosine for different types. Use template versions whenever it's appropriate.
Implement sincos using a template.
#include <iostream>
#include <cmath>
// Generic implementation of PI().
template <class Real>
Real PI(Real const& dummy)
{
return (Real)M_PI;
}
// Add overloads of PI for your own types.
// Generic implementation of Sine().
template <class Real>
Real Sine(Real const& x)
{
return std::sin(x);
}
// Add overloads of Sine for your own types.
// Generic implementation of Cosine().
template <class Real>
Real Cosine(Real const& x)
{
return std::cos(x);
}
// Add overloads of Cosine for your own types.
// Generic implementation of sincos().
template <class Real>
Real sincos(const Real& x)
{
return Sine(PI(Real{0})*x) * Cosine(PI(Real{0})*x);
}
int main()
{
double s1 = sincos<double>(0.2);
float s2 = sincos<float>(0.15);
std::cout << "s1: " << s1 << std::endl;
std::cout << "s2: " << s2 << std::endl;
}
Output:
s1: 0.475528
s2: 0.404509
The following pattern will look both in namespace std and the argument-dependent namespace:
template<typename T>
T sincos(T x)
{
using namespace std;
return sin(x) * cos(x);
}
This does assume that the relevant types were implemented properly, i.e. in their own namespace with the standard names for the operators. If not you might need to do the wrapping:
namespace myReals
{
using Real = ::myReal;
using cos = ::myRealCos;
using sin = ::myRealSin;
}
Related
I want to write a template function that will work on both double and float, something like that:
template <typename T>
constexpr auto someCalc(const T x) {
return x * 4.0 + 3.0;
}
My issue is that both 4.0 and 3.0 are doubles literal, thus I get the following error on clang:
error: implicit conversion increases floating-point precision: 'const float' to 'double' [-Werror,-Wdouble-promotion]
Is there an elegant way to write this code without having the up-conversion to double? The best I can come up with, is this
template <typename T>
constexpr auto someCalc(const T x) {
constexpr T four = 4.0;
constexpr T three = 3.0;
return x * four + three;
}
which I find less readable and harder to maintain for larger/more complicated function.
I would do it this way:
template <typename T>
constexpr auto someCalc(const T x) {
return x * T(4.0) + T(3.0);
}
Or with static_cast, if you prefer it.
While HolyBlackCat's answer is completely viable, here's my two bits:
Your constants are supposed to mean something in that particular situation. They likely are magic numbers with some meaning. The best course of actions is to give them a name, if you want make them maintenable. And in newer C++ it allows them to be templates
#include <iostream>
namespace sdd // some_dirty_details
{
/// #brief: Description of the constant
template <class T>
constexpr T some_constant = static_cast<T>(3.0);
}
int main()
{
std::cout << sdd::some_constant<float> << std::endl;
}
I'm writing a C++ library that contains a lot of function templates I want to explicitly instantiate and export for several type parameters. In my particular case, I have a lot of numeric function templates that I want to separately instantiate and compile for float, double, and long double. They look something like this:
template <typename T>
T calculate_a(T x) { ... }
template <typename T>
T calculate_b(T x, T y) { ... }
// ...
If I have M function templates and N underlying types, then I have M*N explicit instantiations to type out. Is it possible to write these instantiations more concisely?
My current solution is to use a preprocessor macro that performs all instantiations for a given type:
#define EXPLICITLY_INSTANTIATE(T) \
template T calculate_a<T>(T x); \
template T calculate_b<T>(T x, T y); \
// ...
EXPLICITLY_INSTANTIATE(float);
EXPLICITLY_INSTANTIATE(double);
EXPLICITLY_INSTANTIATE(long double);
However, this is suboptimal because it requires me to separately maintain another copy of each function template's signature. Also, if I want to do this in multiple translation units, then I need to separately maintain a list of underlying types in each. (Suppose that C++2a adds a long long double type that I want to support; I'll have to add EXPLICITLY_INSTANTIATE(long long double); to every file.)
Another possible approach is to gather up all of my functions into a (static-only) template class:
template <typename T>
class calculate {
T a(T x) { ... }
T b(T x, T y) { ... }
};
template class calculate<float>;
template class calculate<double>;
template class calculate<long double>;
This solves the first problem of separately maintaining two copies of each signature, but requires me to change each call of calculate_a into calculate::a<T>. It doesn't address the second problem.
You can avoid repeating function signatures by instantiating templates via taking their addresses:
// forward declarations in a header file
template<typename T>
T square(T num);
template<typename T>
T add(T left, T right);
// implementations and instantiations in a single implementation file
template<typename T>
T square(T num) {
return num * num;
}
template<typename T>
T add(T left, T right) {
return left + right;
}
// instantiations for specific types
#include <tuple>
template<typename... Ts>
auto instantiate() {
static auto funcs = std::tuple_cat(std::make_tuple(
add<Ts>,
square<Ts>
)...);
return &funcs;
}
template auto instantiate<int, double>();
The overhead here is a single array of pointers to all instantiated functions, example on godbolt.
This is what X Macros are made for. It works quite simply.
You have a file that contains all of the types you want to apply this to. Let's call it "type_list.inc". It would look like this:
X(float)
X(double)
X(long double)
When you want to perform some operation over that list of types, you #include the file, but around the point of inclusion, you #define the macro X to do the operation you want to perform:
#define X(T) \
template T calculate_a<T>(T x); \
template T calculate_b<T>(T x, T y); \
// ...
#include "type_list.inc"
#undef X
You still have to maintain two sets of function prototypes. But you only need to maintain one list of types.
I haven't clearly communicated my intent in the question. The purpose of my explicit instantiations isn't to limit the types that these functions can be called with, but to inform the compiler to produce executable code for float, double, and long double
Well... if all your types are default constructible (as float, double and long double)... using folding in a template foo() function as follows
template <typename ... Ts>
void foo ()
{ ((calculate_a(Ts{}), calculate_b(Ts{}, Ts{})), ...); }
and calling foo() with desidered types
foo<float, double, long double>();
should work, I suppose.
The following is a full compiling example
template <typename T>
T calculate_a (T x)
{ return x; }
template <typename T>
T calculate_b (T x, T y)
{ return x+y; }
template <typename ... Ts>
void foo ()
{ ((calculate_a(Ts{}), calculate_b(Ts{}, Ts{})), ...); }
int main ()
{
foo<float, double, long double>();
}
Use regular overloading for the types you want and have them call the function template, as in:
float calculate_a(float x) { return calculate_a<float>(x); }
float calculate_b(float x, float y) { return calculate_b<float>(x, y); }
double calculate_a(double x) { return calculate_a<double>(x); }
double calculate_b(double x, double y) { return calculate_b<double>(x, y); }
long double calculate_a(long double x) { return calculate_a<long double>(x); }
long double calculate_b(long double x, long double y) { return calculate_b<long double>(x, y); }
I would like to define custom constants which can be used for an arbitrary type (e.g. float, double, etc.). As an example, suppose I wish to define a constant whose value is pi.
The obvious solution is to use #define pi 3.14159265359, but then pi would not be in a namespace and I risk a name collision. I'm not using C++14 so I can't use a variable template. The best way I can think to do this is like the following:
#include <iostream>
using namespace std;
namespace constants {
template<typename T> T pi() {
return 3.14159265359;
}
}
int main() {
float pitest = 0;
pitest = constants::pi<float>();
cout << pitest << endl;
cout << constants::pi<long double>() << endl;
cout << constants::pi<int>() << endl;
return 0;
}
I can now define these constants within a namespace and I can use an arbitrary (numerical) type as desired. However, there are at least two undesirable features of this:
It requires a function call which should not be necessary (it's just a constant!).
I have to specify the type in the function call, even if the function is returning to a variable of known type. For example, in the above code I have to use pitest = constants::pi<float>(); rather than simply pitest = constants::pi(); even though pitest is obviously a float.
Is there a better way to do this?
Why not use a special object with automatic conversion to any type?
static struct {
template<class T> operator T() const constexpr
{ return (T)3.14159265359; }
} pi;
You might even add specializations for bigger types, arbitrary-precision-arithmetic, formula-systems, whatever.
static struct { template<class T> operator T() const constexpr { return 3.14; } } pi;
is the first step.
template<class T> struct type {};
template<class T> constexpr T get_pi( type<T> ) { return 3.14; }
static struct { template<class T> operator T() const constexpr { return get_pi( type<T>{} ); } } pi;
is the second type -- now you can add new overloads for new types without having to specialize. All pi does is do the magic casting.
Sadly, this requires that we match the type exactly -- a new overload for int won't solve long, or a new overload for double won't solve float.
But this is C++, we can do it!
template<class T> struct contra_type {
constexpr contra_type(contra_type&&) {};
template<class U, class=typename std::enable_if< std::is_convertible< T, U >::value >::type>
constexpr contra_type( contra_type<U>&& ) {}
};
template<class T> constexpr auto get_pi( type<T>, ... )->decltype(T(3.14)) { return T(3.14); }
static struct { template<class T> operator T() const constexpr { return get_pi( contra_type<T>{} ); } } pi;
is the next step. Now we can add overloads for get_pi( type<bignum> ) and such and have it work. In fact, anything implicitly convertable from bignum will call get_pi( type<bignum> ) automatically.
Not sure how to enable ADL -- if I take a T*, I'll get covariant overloads not contravariant overloads (and as we are actually overloading on return type, that isn't want I want).
contra_type<U> is convertible to contra_type<T> if and only if T is convertable to U. This means that pi_func( contra_type<Foo>{} ) will attempt to find a pi_func that takes a type that can convert to Foo, and call that instead.
The ... overload gives us a default implementation that matches everything exactly, but because it has ... it will be preferred to call any other function instead of it that matches.
i wrote a template class that should work for double and std::complex. as it is suppose to be, all my methods are in the .hpp file. all but one. i had to specialize a method because at some place i have to compute the square of a double or the norm of a std::complex. more explicitly for the "double specialization" (A):
double a(2.0);
double b(0.0);
b = a*a;
for the "complex specialization" (B):
std::complex<double> a(2.0,3.0);
double b(0.0);
b = std::norm(a);
my questions are :
is there a way to avoid theses specializations by using a function that works for both double and complex ? (because the std::norm works only for complex...)
or the only solution is to cast the double a of the specialization (A) into a complex and then use only the specialization (B) as a general template (working for both double and complex)?
You can minimise the divergent case by introducing your own function as a square/norm wrapper:
template <typename T>
double square_or_norm(T x);
template<>
inline double square_or_norm(double x) { return x * x; }
template <>
inline double square_or_norm(std::complex<double> x) { return norm(x); }
Then, use it inside the function which needs it:
template <typename T>
T computation(T x) {
return some_code_with(x, square_or_norm(x));
}
You could define two function template overloads (when it comes to function templates, overloading is usually preferable to specialization) called compute_norm(), one accepting std::complex and one accepting unconstrained types. The unconstrained template would invoke operator *, while the constrained template would invoke std::norm().
#include <complex>
template<typename T>
double compute_norm(T t)
{ return t * t; }
template<typename T>
double compute_norm(std::complex<T> const& t)
{ return std::norm(t); }
Then, your generic code that can work both with a double and with a complex<double> would call compute_norm():
#include <iostream>
template<typename T>
void foo(T&& t)
{
// ...
double n = compute_norm(std::forward<T>(t));
std::cout << n << std::endl;
// ...
}
For instance, the following program:
int main()
{
double a(2.0);
foo(a);
std::complex<double> c(2.0, 3.0);
foo(c);
}
Will output:
4
13
Here is a live example.
If you have a conforming standard library, there is an overload of std::norm for floating point types:
26.4.9 Additional overloads [cmplx.over]
The following function templates shall have additional overloads:
arg norm
conj proj
imag real
The additional overloads shall be sufficient to ensure:
If the argument has type long double, then it is effectively cast to complex.
Otherwise, if the argument has type double or an integer type, then it is effectively cast to complex<
double>.
Otherwise, if the argument has type float, then it is effectively cast to complex.
This should work (and does on gcc 4.7.2)
#include <complex>
#include <iostream>
int main()
{
std::complex<double> c {1.5, -2.0};
double d = 2.5;
std::cout << "|c| = " << std::norm(c) << '\n'
<< "|d| = " << std::norm(d) << '\n';
}
Why not just use function overloading?
double myNorm(double);
double myNorm(std::complex<double>);
double myNorm(double x) {
return x * x;
}
double myNorm(std::complex<double> x) {
return std::norm(x);
}
You can put the implementation in your .cpp or (when inlined) in your header file.
I'm new to C++ and I'm trying to use template but I got problems.
What I'm trying to do is: try to calculate square of a number using template, and the number may be basic data types like int, float, as well as complex numbers. I also implemented a complex class using template, and the codes are as follows:
template <typename T>
class Complex {
public:
T real_;
T img_;
Complex(T real, T img) : real_(real), img_(img) { }
};
template <typename T>
T square(T num) {
return num * num;
}
template <>
Complex<typename T> square(Complex<typename T> num) {
T temp_real = num.real_*num.real_ - num.img_*num.img_;
T temp_img = 2 * num.img_ * num.real_;
return Complex(temp_real, temp_img);
}
I tried to use template specialization to deal with the special case, but it gave me error:
using ‘typename’ outside of template
and the error happens on the template specialization method. Please point out my mistakes. Thanks.
It appears that you're trying to partially specialize function templates, which isn't actually possible in C++. What you want instead is to simply overload the function like this:
template<typename T>
T square(T num) // Overload #1
{
return num * num;
}
template<typename T>
Complex<T> square(Complex<T> num) // Overload #2
{
T temp_real = num.real_*num.real_ - num.img_*num.img_;
T temp_img = 2 * num.img_ * num.real_;
return Complex<T>(temp_real, temp_img);
}
Informally, the compiler will always pick overload #2 over overload #1 when the argument is of type Complex<T> because it's a better match.
Another way to make this work is to overload the multiplication operator for the Complex<> class using the definition of multiplication for complex numbers. This has the advantage of being more general and you can extend this idea to other operators.
template <typename T>
class Complex
{
public:
T real_;
T img_;
Complex(T real, T img) : real_(real), img_(img) {}
Complex operator*(Complex rhs) // overloaded the multiplication operator
{
return Complex(real_*rhs.real_ - img_*rhs.img_,
img_*rhs.real_ + real_*rhs.img_);
}
};
// No overload needed. This will work for numeric types and Complex<>.
template<typename T>
T square(T num)
{
return num * num;
}
Since you are new to C++, I highly recommend that you pick up a good introductory C++ book. Templates and operator overloading aren't exactly beginner's topics.