How correctly to remove code repetition in template function - c++

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

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;
}

C++ enable_if_t SFINAE

I am trying to understand why this piece is code isn't working as expected
#include <cstdio>
#include <vector>
#include <type_traits>
using namespace std;
struct Foo {
};
template<typename T, typename = void>
void compare(const T&a, const T&b) {
cout << "default" << endl;
}
template<typename T, std::enable_if_t<std::is_same<T, Foo>::value>>
void compare(const T& a, const T &b) {
cout << "In object" << endl;
}
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}
In all cases "default" is printed. For the last case I would expect that the 2nd function to get invoked.
You didn't specialize compare (it's impossible to partially specialize a function template anyway). Instead you provide an overload.
And the overload is always illegal:
Either enable_if_t is not defined.
Or it specifies a non-type template parameter of type void.
So it's never going to be called, because SFINAE discards it in favor of the always valid overload at the top.
Specialization is usually the wrong answer for function templates. Instead you should delegate to a class template, which will behave as expected upon true specialization:
template<typename T, typename = void>
struct compare_impl {
static void execute(T const& l, T const& r) { /*Base case code*/ }
};
template<typename T>
struct compare_impl<T, std::enable_if_t<std::is_same<T, Foo>::value>> {
static void execute(T const& l, T const& r) { /*Special case code*/ }
};
template<typename T>
void compare (T const& l, T const& r) { compare_impl<T>::execute(a, b); }
Using a class template is a valid solution, here's another way to achieve this result with tag dispatching:
template <class T>
void compare(const T & l, const T & r, std::true_type)
{
cout << "In object" << endl;
}
template <class T>
void compare(const T & l, const T & r, std::false_type)
{
cout << "default" << endl;
}
template <class T>
void compare(const T & l, const T & r)
{
compare(l, r, std::is_same<T, Foo>{});
}
Deferring to a specialised function object allows a lot of flexibility on how argument types and/or whether they are rvalues or lvalues.
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
// general case
template<class T>
struct compare_impl
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "default" << endl;
}
};
// compare interface
template<class T, class U>
auto compare(T && a, U && b)
{
using ctype = std::common_type_t<std::decay_t<T>, std::decay_t<U>>;
auto impl = compare_impl<ctype>();
return impl(a, b);
}
// now specialise for objects for which we want custom behaviour
struct Foo {
};
template<>
struct compare_impl<Foo>
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "In object" << endl;
}
};
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}

C++ template operator+ overloading in inner class

How do I overload operator+ for inner class of a class template? I've searched for hours now and I can't find an answer. This is a minimal example that does not work:
#include <iostream>
using namespace std;
template <class T>
struct A
{
struct B
{
T m_t;
B(T t) : m_t(t) {}
};
};
template <class T>
typename A<T>::B operator+(typename A<T>::B lhs, int n)
{
lhs.m_t += n;
return lhs;
}
int main(int argc, char **argv)
{
A<float> a;
A<float>::B b(17.2);
auto c = b + 5;
cout << c.m_t << endl;
return 0;
}
If I compile like this, I get error: no match for ‘operator+’ (operand types are ‘A<float>::B’ and ‘int’)
I found somewhere that operator+(A<T>::B, int) should be declared, so if I add the following:
struct B;
friend B operator+(typename A<T>::B lhs, int n);
after struct A {, I get a linker error.
If I don't try to call b+5, the program compiles correctly.
How did they (STL makers) program vector<T>::iterator operator+ with an int? I can't find it anywhere (and it's kind of hard to read stl_vector.h)!
Thank you.
The problem you're facing is that when you declare a function template like:
template <class T>
typename A<T>::B operator+(typename A<T>::B lhs, int n)
typename A<T>::B lhs is a non-deduced context. There is no way for the compiler to determine what T is in that context, so it doesn't try, so it cannot find your operator+. Consider a reduced example like:
template <class T> void foo(typename T::type );
struct A { using type = int; };
struct B { using type = int; };
foo(0); // what would T be?
// how many other possible T's are there that fit?
In order for template deduction to succeed with non-deduced contexts, the template type parameter must be explicitly specified. In this case, this monstrosity of a syntax compiles:
auto c = ::operator+<float>(b, 5);
But probably isn't your intended usage!
You will need to declare the operator+ within struct B:
struct B
{
T m_t;
B(T t) : m_t(t) {}
// member
B operator+(int n) {
return B(m_t + n);
}
// or non-member, non-template friend
friend B operator+(B lhs, int n) {
lhs.m_t += n;
return lhs;
}
};
Maybe you could do something like this:
#include <iostream>
#include <type_traits>
using namespace std;
template <class T>
struct A
{
struct B
{
typedef A<T> OuterType;
T m_t;
B(T t) : m_t(t) {}
};
};
template <class T>
typename T::OuterType::B operator+(T lhs, int n)
{
lhs.m_t += n;
return lhs;
}
int main(int argc, char **argv)
{
A<float> a;
A<float>::B b(17.2);
auto c = b + 5;
cout << c.m_t << endl;
return 0;
}
Edit: This would work as the T is deducable from first operand of expression (b + 5) and works only for the structs that contains OuterType struct defined that has subtype B as an inner struct. You can test if this struct is the same as T using typename enable_if<is_same<T, typename T::OuterType::B>::value, T>::type instead of result type: typename T::OuterType::B

Conditional compilation based on template values?

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;
}