how to get max using template metaprograming using variadic template class - c++

Below I am trying to get a simple max value from a list of traits containing SOME_VAL at compile time:
struct A_Traits { enum { SOME_VAL=5 }; };
struct B_Traits { enum { SOME_VAL=6 }; };
struct C_Traits { enum { SOME_VAL=4 }; };
template<typename T>
T MAX(T t1, T t2)
{
return t1>t2?t1:t2;
}
template<typename T, typename... Args>
struct max_calculator
{
enum { MAX_VAL = MAX(max_calculator<typename T::SOME_VAL>::MAX_VAL, // line 65
max_calculator<Args...>::MAX_VAL) };
};
template<typename T>
struct max_calculator<T>
{
enum { MAX_VAL = T::SOME_VAL };
};
int main()
{
cout << max_calculator<A_Traits>::MAX_VAL << endl;
cout << max_calculator<A_Traits, B_Traits>::MAX_VAL << endl; // line 79
}
However I get a compile error of:
variadic.cpp: In instantiation of 'struct max_calculator<A_Traits, B_Traits>':
variadic.cpp:79:47: required from here
variadic.cpp:65:10: error: no type named 'SOME_VAL' in 'struct A_Traits'
Any idea whats wrong or better ideas to do the same at compile time?

std::max has a constexpr overload for std::initialiser_list<T>, you can expand your pack in one place
template<typename... Args>
struct max_calculator
{
enum { MAX_VAL = std::max({ Args::SOME_VAL... }) };
};

For the benefit of everyone visting this later - the fixed program based on Piotr's answers in the comments section is:
struct A_Traits { enum { SOME_VAL=5 }; };
struct B_Traits { enum { SOME_VAL=6 }; };
struct C_Traits { enum { SOME_VAL=4 }; };
template<typename T>
constexpr T MAX(T t1, T t2)
{
return t1>t2?t1:t2;
}
template<typename T, typename... Args>
struct max_calculator
{
enum { MAX_VAL =MAX<uint8_t>(max_calculator<T>::MAX_VAL,
max_calculator<Args...>::MAX_VAL) };
};
template<typename T>
struct max_calculator<T>
{
enum { MAX_VAL = T::SOME_VAL };
};
int main()
{
cout << max_calculator<A_Traits>::MAX_VAL << endl;
cout << max_calculator<A_Traits, B_Traits>::MAX_VAL << endl;
cout << max_calculator<A_Traits, B_Traits, C_Traits>::MAX_VAL << endl;
}

Related

C++ Template instancing with mutual reference

Is there a way to allow two or more templates instanciations to mutually refer to each other ?
Example :
/* invalid C++ */
/* we suppose MyTemplate1 and MyTemplate2 are declared */
typedef MyTemplate1<MyInstance2> MyInstance1;
typedef MyTemplate2<MyInstance1> MyInstance2;
I suppose there is none, still asking just in case I missed something.
Adding more precision, I want to achieve such a construction :
/* invalid C++ */
#include <iostream>
template <typename typeT> struct MyStruct1 {
static void print(unsigned i) {
std::cout << "MyStruct1 : " << i << std::endl;
if (i > 0) {
typeT::print(i - 1);
}
}
};
template <typename typeT> struct MyStruct2 {
static void print(unsigned i) {
std::cout << "MyStruct2 : " << i << std::endl;
if (i > 0) {
typeT::print(i - 1);
}
}
};
/* of course this is invalid, since you can't reference MyInstance2
before it is declared */
typedef MyStruct1<MyInstance2> MyInstance1;
typedef MyStruct2<MyInstance1> MyInstance2;
int main() {
MyInstance1::print(5);
return 0;
}
output should be :
MyStruct1 : 5
MyStruct2 : 4
MyStruct1 : 3
MyStruct2 : 2
MyStruct1 : 1
MyStruct2 : 0
Please note I'm not trying to achieve a similar output, but a similar construct, where two (or more) templates instances refer to each other
with as few as possible additional code : it shall be easy to do mutual reference instantiation. However, for the implementation code of the two templates, I don't care if they are complicated.
Here's a solution that at least gives the correct output. If it's also a viable solution for your use case is not very clear though but maybe it can at least help you clarify your question a bit more.
#include <iostream>
template <template <typename> typename TemplateT> struct TemplateType {
template <typename typeT>
static void print(unsigned i) {
TemplateT<typeT>::print(i);
}
};
template <typename typeT> struct MyStruct1 {
static void print(unsigned i) {
std::cout << "MyStruct1 : " << i << std::endl;
if (i > 0) {
typeT::template print<TemplateType<MyStruct1>>(i - 1);
}
}
};
template <typename typeT> struct MyStruct2 {
static void print(unsigned i) {
std::cout << "MyStruct2 : " << i << std::endl;
if (i > 0) {
typeT::template print<TemplateType<MyStruct2>>(i - 1);
}
}
};
typedef MyStruct1<TemplateType<MyStruct2>> MyInstance1;
int main() {
MyInstance1::print(5);
return 0;
}
One way is to use class forward declaration:
template<typename T> class M
{
static int foo(int i) { return i ? T::foo(i - 1) : 0; }
};
struct A;
struct B;
struct A : M<B>{};
struct B : M<A>{};
Not same code exactly but you have recursion.
I finally found a satisfying construct, which involves using a tierce struct acting as a context to declare subs elements. It isn't forcibly the best solution for anyone, and I will probably have to adapt it a bit more to fit my very need, but here is the code :
#include <iostream>
#include <type_traits>
template <typename K, typename T> struct TypePair {
typedef K key;
typedef T type;
};
template <typename Context, typename P0, typename... PN> struct TypeMap {
template <typename K> struct get {
typedef typename std::conditional<
std::is_same<typename P0::key, K>::value,
typename P0::type::template actual<Context>,
typename TypeMap<Context, PN...>::template get<K>::type>::type type;
};
};
struct TypeNotFound {};
template <typename Context, typename P> struct TypeMap<Context, P> {
template <typename K> struct get {
typedef
typename std::conditional<std::is_same<typename P::key, K>::value,
typename P::type::template actual<Context>,
TypeNotFound>::type type;
};
};
/* defining a context to link all classes together */
template <typename... TN> struct Context {
template <typename K> struct Access {
typedef typename TypeMap<Context<TN...>, TN...>::template get<K>::type type;
};
};
/* templates we want to cross ref, note that context is passed as a parameter*/
template <typename ContextT, typename Id2> struct MyStruct1Actual {
static void print(unsigned i) {
std::cout << "MyStruct1 : " << i << std::endl;
if (i > 0) {
ContextT::template Access<Id2>::type::print(i - 1);
}
}
};
template <typename ContextT, typename Id1> struct MyStruct2Actual {
static void print(unsigned i) {
std::cout << "MyStruct2 : " << i << std::endl;
if (i > 0) {
ContextT::template Access<Id1>::type::print(i - 1);
}
}
};
/* wrappers to not have to always pass context when instancing templates */
template <typename type> struct MyStruct1 {
template <typename ContextT> using actual = MyStruct1Actual<ContextT, type>;
};
template <typename type> struct MyStruct2 {
template <typename ContextT> using actual = MyStruct2Actual<ContextT, type>;
};
/* Enum and dummy id, could simply use Enum actually, but using classes a Id
can prove to be more elegant with complex structures, expecially as it could be
used to automatically create pairs instead of having to precise Id */
enum Ids : int { Struct1, Struct2 };
template <Ids id> struct Id {};
// instancing all stuff withing context
// clang-format off
typedef Context<
TypePair< Id<Struct1>, MyStruct1< Id<Struct2> > >,
TypePair< Id<Struct2>, MyStruct2< Id<Struct1> > >
> Ctx;
// clang-format on
typedef Ctx::Access<Id<Struct1>>::type S1;
int main() {
S1::print(5);
return 0;
}
Shortening names an giving more meaning than Context or TypePair will be mandatory, but the idea is here.

Using SFINAE to check whether function is constexpr or not

I want to check whether a function can be evaluated during compilation. I found this, but I don't understand the concept completely. I have a few doubts:
What is the role of the following line in the code?
template<int Value = Trait::f()>
Every time when I need to check whether the function is compile-time evaluable, Do I need to make it a member function of some struct?
PS
I am copying the code in the link, just for convenience.
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
Here is just a quick example of what you can get with std::void_t to tackle your point 2 that can be generic in some way...
#include <iostream>
#include <type_traits>
int f() {
return 666;
}
constexpr int cf(int, double) {
return 999;
}
template <auto F>
struct indirection {
};
template<typename F, class = std::void_t<> >
struct is_constexpr : std::false_type { };
template<typename F, typename... Args>
struct is_constexpr<F(Args...),
std::void_t<indirection<F(Args{}...)>>
> : std::true_type { };
int main()
{
std::cout << is_constexpr<decltype(f)>::value << std::endl;
std::cout << is_constexpr<decltype(cf)>::value << std::endl;
};
Demo here

passing template for later use in other struct/class context

I have some classes which need to define a template which can be used in generic code parts as type later.
In real world code the forwarded templates have a lot more parameters and it is not really nice to read the code.
Q: Is it possible to define the template in some syntax instead of writing it as alias template as given in the following example? I simple would avoid repeating of all the template parameters two times of each alias declaration.
The real world template also have some non type template parameters so simply using <PARMS...> will not work.
Example:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
struct UseA
{
// using the alias template works as expected, but...
template < typename T>
using USE = A<T>;
// is there any chance to write something like:
// using USE = A;
// to simply avoid replication of template parameters?
};
struct UseB
{
template < typename T>
using USE = B<T>;
};
int main()
{
UseA::USE<int>::Do(1);
UseB::USE<std::string>::Do("Hallo");
}
What you are asking cannot be done. You always have to define the whole type list. The reason is, that one could have default overloads for the same type. For example, in the following A<int, 3>, A<int> and A<> are all valid. The compiler does not know which one you want:
template <class T, int Value = 42>
struct A {};
auto test() {
auto a = A<int, 3>{};
auto b = A<int>{};
auto c = A<>{};
}
If you don't want to write the type lists, I would recommend you to switch to templatizing more of your classes, so they don't need to know about the implementation details. Like:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
template < typename T>
struct Use
{
using USE = T;
};
int main()
{
Use<A<int>>::USE::Do(1);
Use<B<std::string>>::USE::Do("Hallo");
}
Or alternatively, use containers for your non template type values:
#include <iostream>
template < int Value >
struct INT
{
static constexpr int value = Value;
};
template < bool Value >
struct BOOL
{
static constexpr bool value = Value;
};
template < typename T, typename Value >
struct A
{
static void Do(T t) { std::cout << "A " << t << Value::value << std::endl;}
};
template < typename T, typename Value>
struct B
{
static void Do(T t) { if (Value::value) std::cout << "B " << t << std::endl;}
};
template <template<typename...> class T, typename ...Param>
using USE = T<Param...>;
int main()
{
USE<A, int, INT<42>>::Do(1);
USE<B, std::string, BOOL<true>>::Do("Hallo");
}

Calling template function from inner class

I have a class like this to call a function depending on the type. I try to compile it, but have error error C2059 syntax error : 'template'
class A
{
call_1()
{
B<type> b;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
return word;
}
};
template<>
struct B<void>
{
template <typename T, typename I>
void say(I i) {
/**/
}
};
}
What am I doing wrong?
First, let's rewrite your example into something that is readable and is closer to being compilable, and we also print "1" or "2" in say() to know which function gets called:
#include <iostream>
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
template<>
struct B<void>
{
template <typename T, typename I>
void say(I i) {
std::cout << "2\n";
}
};
};
OK, so first, you are trying to specialize B inside of A. This is not allowed, so let't move it outside of A:
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename T, typename I>
void say(I i) {
std::cout << "2\n";
}
};
Next up, you are using the same template parameter (T) in both B and say(). You don't need to repeat T, so let's delete it:
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename I>
void say(I i) {
std::cout << "2\n";
}
};
Finally, call_1() cannot be defined before the specialization of A::B, so we need to move it outside too:
using type = int;
class A {
void call_1();
template<class T>
struct B
{
template <typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename I>
void say(I i) {
std::cout << "2\n";
}
};
void A::call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
This should now compile and do what you want. Calling call_1() will print 1. If you change the type from int to void:
using type = void;
it will print 2.

Problem with SFINAE

Why this code (fnc value in class M) do not get resolved by SFINAE rules? I'm getting an error:
Error 1 error C2039: 'type' : is not a member of
'std::tr1::enable_if<_Test,_Type>'
Of course type is not a member, it isn't defined in this general ver of enable_if but isn't the whole idea behind this to enable this ver of fnc if bool is true and do not instantiate it if it's false? Could please someone explain that to me?
#include <iostream>
#include <type_traits>
using namespace std;
template <class Ex> struct Null;
template <class Ex> struct Throw;
template <template <class> class Policy> struct IsThrow;
template <> struct IsThrow<Null> {
enum {value = 0};
};
template <> struct IsThrow<Throw> {
enum {value = 1};
};
template <template <class> class Derived>
struct PolicyBase {
enum {value = IsThrow<Derived>::value};
};
template<class Ex>
struct Null : PolicyBase<Null> { };
template<class Ex>
struct Throw : PolicyBase<Throw> { } ;
template<template< class> class SomePolicy>
struct M {
//template<class T>
//struct D : SomePolicy<D<T>>
//{
//};
static const int ist = SomePolicy<int>::value;
typename std::enable_if<ist, void>::type value() const
{
cout << "Enabled";
}
typename std::enable_if<!ist, void>::type value() const
{
cout << "Disabled";
}
};
int main()
{
M<Null> m;
m.value();
}
SFINAE does not work for non-template functions. Instead you can e.g. use specialization (of the class) or overload-based dispatching:
template<template< class> class SomePolicy>
struct M
{
static const int ist = SomePolicy<int>::value;
void value() const {
inner_value(std::integral_constant<bool,!!ist>());
}
private:
void inner_value(std::true_type) const { cout << "Enabled"; }
void inner_value(std::false_type) const { cout << "Disabled"; }
};
There is no sfinae here.
After M<Null> is known the variable ist is known also.
Then std::enable_if<ist, void> is well-defined too.
One of your function is not well-defined.
SFINAE works only for the case of template functions.
Where are template functions?
Change your code to
template<int> struct Int2Type {}
void value_help(Int2Type<true> ) const {
cout << "Enabled";
}
void value_help(Int2Type<false> ) const {
cout << "Disabled";
}
void value() const {
return value_help(Int2Type<ist>());
}