Conditional compilation based on template values? - c++

the question posed in:
Type condition in template
is very similar, yet the original question wasn't quite answered.
#include "stdafx.h"
#include <type_traits>
class AA {
public:
double a;
double Plus(AA &b) {
return a + b.a;
}
};
template<class T> double doit(T &t) {
if (std::is_same<T, AA>::value)
return t.Plus(t);
else
return t + t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}
This doesn't compile, nor did I expect it to. Is something like this possible? Being, based on the template value, I want some code to be compiled, and others not. Here, 'double' doesn't have a method called "Plus" and class "AA" doesn't override the '+' operator. Operator overloading isn't always desirable when considering subtle semantics to the operations, so I'm looking for an alternative. I'd prefer to do #ifdef's (truly conditional compilation as posed in the ref'd question), but based on template values.

Since C++17 there is static if which is called if-constexpr. The following compiles fine since clang-3.9.1 and gcc-7.1.0, latest MSVC compiler 19.11.25506 handles well too with an option /std:c++17.
template<class T> double doit(T &t) {
if constexpr (std::is_same_v<T, AA>)
return t.Plus(t);
else
return t + t;
}

What you want is a static if. C++ doesn't have it. There are many ways to work around it, none as good as native support. In addition to the methods specified in the other two answers, you could try tag dispatch.
template<class T> double doitImpl(T &t, std::true_type) {
return t.Plus(t);
}
template<class T> double doitImpl(T &t, std::false_type) {
return t+t;
}
template<class T> double doit(T &t) {
return doitImpl(t, std::is_same<T, AA>);
}

Overloading?
template<class T>
double doit(T &t) {
return t + t;
}
double doit(AA &t) {
return t.Plus(t);
}
Explicit specialization is also possible, though superfluous:
template<class T>
double doit(T &t) {
return t + t;
}
template<>
double doit<>(AA &t) {
return t.Plus(t);
}

double doit(AA &t) {
return t.Plus(t);;
}
template<class T> double doit(T &t) {
return t + t;
}
Your code doesn't work because if the template is deduced as AA then t + t inside the body is ill-formed. On the other hand if T is deduces as double then t.Plus(t) becomes ill-formed.
To better understand what is happening:
A template is instantiated for each template type is called with.
doIt(a) instantiates doIt with T = double:
double doIt<double>(double &t) {
if (false)
return t.Plus(t); // <-- syntax error
else
return t + t;
}
doIt(aa) instantiates doIt with T = AA:
double doIt<AA>(AA &t) {
if (true)
return t.Plus(t);
else
return t + t; // <-- syntax error
}
You should avoid specializing function templates because functions overload. You can read this excellent Herb Sutter article: Why Not Specialize Function Templates?

This code compiles and runs:
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;
class AA {
public:
double a;
AA Plus(AA &b) {
AA _a;
_a.a = a + b.a;
return _a;
}
};
template<class T>
typename disable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t.Plus(t);
}
template<class T>
typename enable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t+t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}

Related

How do I constrain the parameter(s) of a functor using concepts?

I have the following concept:
template <typename T>
concept FunctorInt = requires(T a, int b) {
a.operator()(b); //require the () operator with a single int parameter.
};
I use this in the following function:
template <FunctorInt functor_t>
void for_each_sat_lit(const int ClauseIndex, functor_t functor) {
auto LitIndex = -1;
uint32_t Count = h_SatisfiedLitCount[ClauseIndex];
if constexpr (!satlit) { Count = h_LiteralsInClauseCount[ClauseIndex] - Count; }
for (uint32_t dummy = 0; dummy < Count; dummy++) {
LitIndex = NextSetBit(h_SATLiteralBits[ClauseIndex], LitIndex);
functor(LitIndex); //LitIndex must be an int.
}
}
This compiles. However, when I try and break the code by changing the concept to
template <typename T>
concept FunctorInt = requires(T a, float b) {
a.operator()(b); //I intend to require the () operator with a single float parameter.
};
It still compiles, meaning it did not constrain the functor at all.
How do I constrain a functor so that it can only have a single int parameter?
MSVC: concepts.cpp
#include <concepts>
template <typename T>
concept FunctorInt = requires(T a, int b) {
a.operator()(b); //require the () operator with a single int parameter.
};
template <typename T>
concept FunctorFloat = requires(T a, float b) {
a.operator()(b); //require the () operator with a single float parameter.
};
void Loop(FunctorInt auto functor) {
for (auto i = 0; i < 10; i++) {
functor(i);
}
}
void LoopShouldNotCompile(FunctorFloat auto functor) {
for (auto i = 0; i < 10; i++) {
functor(i); //<< i is not a float.
}
}
int main(const int argc, const char* argv[]) {
Loop( [](int a){ printf("%i", a); });
LoopShouldNotCompile([](float a){ printf("%f", a); });
}
If I change the definitions of FunctorInt and FunctorFloat using std::invocable, the same problem persists:
concept FunctorInt = std::invocable<int>;
concept FunctorFloat = std::invocable<float>;
Everything still compiles, whereas it should give a compile error on LoopShouldNotCompile.
UPDATE:
I settled on:
template <typename T>
concept FunctorInt =
requires() { [](void(T::*)(int) const){}(&T::operator()); }
|| requires() { [](void(T::*)(int) ){}(&T::operator()); };
Which creates a functor that only allows a single int parameter and doesn't care about const-correctness.
If you want to block a function from accepting any other type that you want, you can use a type matching function template... e.g.
#include <concepts>
template <typename T>
requires std::same_as<T,float>
void func([[maybe_unused]]T f) {}
int main() {
//func(1); // doesn't compile
func(1.0f); // works
//func(1.0); // NB: fails again, because float!=double
}
edit: shorter
void func([[maybe_unused]]std::same_as<float> auto f) {}
Your concept check if the type can be called with an int
but you cannot control promotion/conversion which happens before.
You can check signature of T::operator(), but then previous valid cases (as overload, template function, no exact function but similar (const, volatile, ...)) might no longer work.
For example:
template <typename T>
concept FunctorInt = requires() {
[](void(T::*)(int) const){}(&T::operator());
};
void Loop(FunctorInt auto functor) { /**/ }
int main() {
Loop([](int a){ printf("%i", a); });
Loop([](float a){ printf("%f", a); }); // Not compile
}
Demo

Error in passing function to function template

I have function templates :
template<typename T>
inline T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
template<typename T, typename U>
inline T fun5(U &a)
{
return (T(4.0+a*(-2.0),5.0+ a*3.0));
}
template<typename F, typename T>
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
int main()
{
double val;
min(fun3(fun5),val);
std::cout<<"value = "<<val<<"\n";
return 0;
}
I want to evaluate fun3(fun5(x)) and have functions as shown above. But getting error as no matching function for call to ‘Function5<double>::fun5(<unresolved overloaded function type>)’ obj1(o5.fun5(o3.fun3),-2.0,0.0,location,value);
Can someone explain how can I pass function to min()?
What will change if all these functions were class templates like:
template<typename T>
class Fun3 {
inline T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
};
template<typename T, typename U>
class Fun5 {
inline T fun5(U &a)
{
return (T(4.0+a*(-2.0),5.0+ a*3.0));
}
};
template<typename F, typename T>
class Min {
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
};
int main()
{
double val;
Fun5<double> o5;
Fun3<decltype (o5.fun5)> o3;
Min<???,decltype (o5.fun5)> obj; //What is here?
obj(o3.fun3(o5.fun5),val);
std::cout<<"value = "<<val<<"\n";
return 0;
}
I don't know what will go to commented line.
How can I use a function object (functor) here?
I want to evaluate fun3(fun5(x))
min([](auto x){ return fun3(fun5(x)); }, val);
There's no function composition in C++ standard library (though it can be defined with some effort.)
If you really want fun, at least try lambdas. They are simple.
I'd say stay away from templates in the way you want to use them. I am assuming you want a simple happy life to focus on productive thing and I may be wrong. Pardon.
Still, I worked on your code a bit and would say that don't confuse template and macros. It looks like the case at least to me.
Note that the template actually instantiate the code and for that all you can pass is arguments to whatever types and specify those types while template instantiating.
Here is a code sample at ideone - not exactly same but to show how something can be done.
For min(fun3(fun5),val);
If you really want fun3 behavior, pass it. Dont expect the result to be passed just like it works for macro.
.
#include <iostream>
#include <cmath>
using namespace std;
typedef double (*_typeofFun1)(double&);
typedef double (*_typeofFun3)(double&, double&);
template<typename T>
T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
template<typename T, typename U>
U fun5(T t, U &a)
{
//return (T(4.0+a*(-2.0),5.0+ a*3.0));
return t(a,a);
}
template <typename T>
T fun1Param(T& arg)
{
return 2*arg;
}
template<typename F, typename T>
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
int main()
{
double val = 1.0;
double d = fun5<_typeofFun3, double> (fun3, val);
fun3<double>(d, val);
min<_typeofFun1>(fun1Param,val);
std::cout<<"value = "<<val<<"\n";
return 0;
}

Using a template function with different returns for different types doesn't work

so I wrote something like that:
class MyClass
{
enum varType {INTEGER, DOUBLE, VECTOR};
int beautiful_integer;
double awesome_double;
std::vector<float> many_floats;
template <class T>
T getVariable(varType type)
{
if(type == INTEGER)
{
return beatiful_integer;
}
if(type == DOUBLE)
{
return awesome_double;
}
if(type == VECTOR)
{
return many_floats;
}
}
...
};
But my compiler throws error "In instantiation of ..." and basically tells me that the return types don't match (and lists all of the unmatched ones, except the right one) and then tries to instantiate it with another type (for example double) and tells me that the return type doesn't match with int and vector of floats.
What am I doing wrong and how to properly write a template function in order to return different types depeneding on the parameter it was called with. For example when I call:
MyClass some_class(); //EDIT: this should be MyClass some_class;
//thanks for pointing it out
int some_number = some_class.getVariable(INTEGER);
I want to assign the value of beautiful_integer to some_number
As alternative, with std:
template <class T>
const T& getVariable() const
{
return std::get<const T&>(std::tie(beautiful_integer, awesome_double, many_floats));
}
template <class T>
T& getVariable()
{
return std::get<T&>(std::tie(beautiful_integer, awesome_double, many_floats));
}
Template parameters are determined at compile time. You can accomplish it by template specializations. Don't use enum. e.g.
class MyClass
{
int beautiful_integer;
double awesome_double;
std::vector<float> many_floats;
template <class T>
T getVariable();
template<>
int getVariable<int>
{
return beatiful_integer;
}
template<>
double getVariable<double>
{
return awesome_double;
}
template<>
std::vector<float> getVariable<std::vector<float>>
{
return many_floats;
}
};
From C++17 you can also use Constexpr If, e.g.
template <class T>
T getVariable()
{
if constexpr (std::is_same_v<T, int>)
{
return beatiful_integer;
}
else if constexpr (std::is_same_v<T, double>)
{
return awesome_double;
}
else if constexpr (std::is_same_v<T, std::vector<float>>)
{
return many_floats;
}
else
{
...
}
}
then
MyClass some_class;
int some_number = some_class.getVariable<int>();
BTW: MyClass some_class(); doesn't do what you expect. See most vexing parse.

How correctly to remove code repetition in template function

I have code like this ("spaceship"-like operator).
template <class T>
int comparator(const T &a, const T &b){
if (a < b){
return -1;
}else if (a > b){
return +1;
}
return 0;
}
inline int comparator(const char *a, const char *b){
return strcmp(a, b); // I never tried this, included just to get the idea
}
inline int comparator(char const a, char const b){
return a - b;
}
inline int comparator(int const a, int const b){
return a - b;
}
How can I easily remove repetition for several signed types (char, short, int, long etc). I tried with SFINAE, but result was not very encouraging.
You can mix together overloading, tag dispatching and templates, as in the following example:
#include<type_traits>
#include<utility>
#include<iostream>
template <class T>
int comparator_(char, const T &a, const T &b){
std::cout << "catch all" << std::endl;
return (a<b)?-1:((a>b)?1:0);
}
template<typename T>
std::enable_if_t<std::is_same<T,int>::value or std::is_same<T,char>::value, int>
comparator_(int, T const a, T const b){
std::cout << "char or int" << std::endl;
return a - b;
}
template<typename... A>
int comparator(A&&... args) {
return comparator_(0, std::forward<A>(args)...);
}
int main() {
comparator(42,0);
comparator('c', 'g');
comparator(42u, 0u);
}
Start by delegating the template function to a template class
template <class T>
int comparator(const T &a, const T &b){
return comparator_impl<T>::comparator(a, b);
}
The default template class implementation is what you already wrote:
template<class T>
class comparator_impl {
public:
static int comparator(const T &a, const T &b){
if (a < b){
return -1;
}else if (a > b){
return +1;
}
return 0;
};
Now, have a separate template class that will be used for signed integral types:
template<class T>
class signed_int_comparator_impl {
public:
static int comparator(T a, T b)
{
return a-b;
}
return 0;
};
And now, specialize the first template class, with the specialization inheriting from the second one:
template<>
class comparator_impl<char> : public signed_int_comparator_impl<char> {};
template<>
class comparator_impl<int> : public signed_int_comparator_impl<int> {};
Lather, rinse, repeat, for remaining signed integer types.
If you want to specialize comparator_impl for a const char *, feel free to do so.
For template specialization, the recommendation is to use template class/struct, not template function. See http://www.gotw.ca/publications/mill17.htm
std::is_integral together with std::is_signed look like the correct tool for your SFINAE. Here is a working example: https://ideone.com/8wm54h

C++ using function template in class template

template <int C> class MyClass;
...
template <int C>
double trans(MyClass<C> &a)
{
//return some double
}
//this is supposed to be function template
template <int C>
double func(MyClass<2> &a)
{
//return some double
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
template <int C>
double otherFunc(MyClass<C> &a)
{
double result;
if(C == SOME_CONSTANT)
result = func(a);
else
result = trans(func(a));
}
What my problem is I want to check template argument C in parameter otherFunc call function template func (return double) instead of member function of class template func(return MyClass). But somehow compiler attempts to func that returns MyClass in
if(C == SOME_CONSTANT)
result = func(a);
this part so I got compiler error (because double = MyClass is not viable). How should I fix this problem?
Hard to elaborate in a comment, so I'll post an answer. To recap what I said in comments:
you can't have an if/else in otherFunc that won't be legal to compile for a specific MyClass instantiation,
you can use the same template specialisation approach to create an alternative otherFunc definition to handle MyClass<2>s.
Sample code:
#include <iostream>
template <int C>
class MyClass
{ };
template <int C>
double trans(const MyClass<C> &a)
{
return C + 700;
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
double func(MyClass<2>& a)
{
return 200.0;
}
template <int C>
double otherFunc(MyClass<C> &a)
{
return trans(func<C>(a));
}
template <>
double otherFunc<2>(MyClass<2>& a)
{
return func(a);
}
int main()
{
MyClass<2> a;
std::cout << otherFunc(a) << '\n';
MyClass<4> b;
std::cout << otherFunc(b) << '\n';
}
Output:
200
703
I guess the reason is that:
if(C == SOME_CONSTANT)
result = func(a);
else
result = trans(func(a));
is checked in the runtime, while template specializations and type check is done during compilation. Those 2 functions:
template <int C>
double func(MyClass<2> &a)
{
//return some double
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
have almost the same signatures - the only difference is that for C==2 is used the one returning double, otherwise used the one returning MyClass<2>. So what you get is:
// for C==2
if(C == SOME_CONSTANT)
result = func<2>(a); // returns double - just like you wanted
else
result = trans<2>(func<2>(a)); // pass double into trans<?>(MyClass<?>), so the parameter cannot be resolved unless you specified some implicit constructor/explicit conversion operator
// for e.g. C==3
if(C == SOME_CONSTANT)
result = func<3>(a); // returns MyClass<2> while result is double
else
result = trans<2>(func<3>(a)); // pass MyClass<2> into trans<?>(MyClass<>) - like you wanted
so basically your code have invalid type for specializations in either first or second case. You can do something like this instead:
template <>
MyClass<2> func<2>(MyClass<2> &a)
{
return a;
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
return MyClass<C-1>;
}
to keep templates type consistent, and then:
if(C == SOME_CONSTANT)
result = trans(a);
else
result = trans(func(a));
It's just an example how to handle it. Things should be easier if you avoid messing with different return typed for different template specializations.