How do I do the following in C++:
template <typename T>
void Foo(T t)
{
...
call Bar(true) if T is of some specific type U or V
call Bar(false) otherwise
...
}
void Bar(bool b)
{
...
}
I could add a redundant template parameter but it would be, well..., redundant.
I could also try to make Bar a template function and specialize it for U and V but that is not my code and the problem would probably just propagate.
I could create a function CallBar that does nothing but to call Bar(false) and specialize it to call Bar(true) for U and V. But the example is actually a bit oversimplified here. The boolean is used in multiple places in the FooLogger sometimes in calls to functions (so there are multiple Bars) sometimes even in ?: conditionals.
What is the best thing to do here?
The idiomatic solution would be to use traits:
template <typename T>
struct BarTraits {
static const bool value = false;
};
template <>
struct BarTraits<U> {
static const bool value = true;
};
template <>
struct BarTraits<V> {
static const bool value = true;
};
template <typename T>
void Foo(T t)
{
...
Bar(BarTraits<T>::value);
...
}
A possible solution using std::is_same:
template <typename T>
void Foo(T t)
{
Bar(std::is_same<T, int>::value || std::is_same<T, char>::value);
}
Related
I'm writing a template class that stores a std::function in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void type because
void call(void type)
becomes undefined.
Specializing it for the void type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type).
So, using the new features of C++ 11, I tried std::enable_if:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t is a type so doesn't require typename before.
p.s.2: you tagged C++11 but your example use std::enable_if_t, that is C++14, and std::is_void_v, that is C++17
If you don't stick to use void, and your intention is to actually able to use Test without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
I have this method
template <typename T>
T GetFnInput(){
//.... here is obtained void * t value from some library
return (T)(t);
}
And I have several template specializations for different types
template <>
uint32 GetFnInput<uint32>(){
return 0;
}
template <>
bool GetFnInput<bool>(){
return true;
}
However, I need a specialization for reference. I have tried this (the code is mess and should not be used in production, this is just for my testing purposes):
template <typename T,
typename BaseT = std::decay<T>::type,
typename std::enable_if <std::is_reference<T>::value == true>::type* = nullptr >
T GetFnInput(){
BaseT * t = new BaseT();
return *t;
}
Plus I have add typename std::enable_if <std::is_reference<T>::value == false>::type* = nullptr > to original (above) GetFnInput()
But it wont compile, end with error:
error C2244: 'GetFnInput': unable to match function definition to an
existing declaration
First problem is that you're missing typename here:
typename BaseT = std::decay<T>::type,
^^^
Once you have that, you have a second problem which is that the call to, say, GetFnInput<int&>() is ambiguous between the original function template GetFnInput<typename> and this new function template GetFnInput<typename, typename, typename>. These two function templates are overloads of each other and are otherwise unrelated.
Typically want you want to do is lift the template parameter into the argument list so it becomes easier to overload on:
template <class T> struct tag { using type = T; };
template <class T>
auto get() -> decltype(get_impl(tag<T>{}))
{
return get_impl(tag<T>{});
}
And then you can write your get_impl function template much easier. Specific types are just specific overloads:
uint32_t get_impl(tag<uint32_t> ) { return 0; }
And reference types is just a template:
template <class T>
T& get_impl(tag<T& > ) { ???; }
Note that return a reference to an allocated pointer sounds like a really bad idea.
You try is an overload, not a specialization.
As function cannot be partial specialized, I suggest to use struct instead:
template <typename T>
struct helper
{
T operator() const { return {}; }
};
template <>
struct helper<bool>
{
bool operator() const { return true; }
};
template <typename T>
struct helper<T&>
{
T& operator() const { static T t; return t; }
};
template <typename T>
T GetFnInput(){
return helper<T>{}();
}
The code below doesn't compile, it complains about "can't convert string to int" when I call func with type Foo and "can't convert int to string" when I call func with type Bar. I thought I already used std::is_same to tell if the type is a Foo or Bar, why this seems to be not working? What would be a better way to do this?
class Foo {
Foo(int foo){}
};
class Bar {
Bar(string foo){}
};
template<typename T>
void func(){
if(std::is_same<T, Foo>::value) {
T t(1);
} else {
T t("aaa");
}
}
func<Foo>();
func<Bar>();
There is no static if in C++, so the code has to be compilable even if the branch is not taken.
You may solve that with specialization:
template<typename T>
void func(){
T t("aaa");
}
template<>
void func<Foo>(){
Foo t(1);
}
Demo
If you want a more general solution than the one proposed by Jarod42, you could use std::enable_if (since c++11):
template<typename T>
typename std::enable_if<std::is_constructible<T, int>::value, void>::type func() {
T t(1);
}
template<typename T>
typename std::enable_if<std::is_constructible<T, const char *>::value, void>::type func() {
T t("abc");
}
This way, the compiler will only generate function where enable_if is true (this is a "almost static if").
You can use std::enable_if to check a lot of things, if you only need to check if the instantiation T(1) is valid you could use SFINAE expressions:
template<typename T>
decltype(T(1), void()) func(){
T t(1);
}
template<typename T>
decltype(T(std::string()), void()) func() {
T t("abc");
}
See also std::is_constructible.
Tag dispatching is the way to go.
template<class T>
void func_impl( std::true_type T_is_Foo ) {
T t(1);
}
template<class T>
void func_impl( std::false_type T_is_Foo ) {
T t("aaa");
}
template<typename T>
void func(){
return func_impl( std::is_same<T,Foo>{} );
}
You can mess around with template function specialization or SFINAE; both are fragile. Template function specialization behaves unlike similar features elsewhere in C++, and SFINAE is impenetrable.
Another option in C++14 is to write a static if using lambdas. This is impenetrable, but at least isolated from your actual code:
struct do_nothing{ template<class...Ts> void operator()(Ts&&...){} };
template<class F_true, class F_false=do_nothing>
auto static_if( std::true_type, F_true&& f_true, F_false&& f_false = do_nothing{} ) {
return std::forward<F_true>(f_true);
}
template<class F_true, class F_false=do_nothing>
auto static_if( std::false_type, F_true&& f_true, F_false&& f_false = do_nothing{} ) {
return std::forward<F_false>(f_false);
}
template<typename T>
void func(){
static_if( std::is_same<T,Foo>{}, [&](auto&&){
T t(1);
}, [&](auto&&){
T t("abc");
})(1);
}
which at least looks like an if statement. static_if returns the second or third argument depending on the static truth value of its first argument.
In this case, we pass in lambdas. We then invoke the return value.
Only the lambda that matches the truth value of the first argument is invoked.
You could use SFINAE and create 2 overloaded functions for different T types
typename = std::enable_if<std::is_same<...>::value>::type
typename = std::enable_if_t<std::is_same<...>::value> // for c++14
template<typename T>
typename std::enable_if<std::is_same<Foo, T>::value>::type
func()
{
T t(4);
}
template<typename T>
typename std::enable_if<!std::is_same<Foo, T>::value>::type
func()
{
T t("4");
}
I have a template that has a function pointer as it's 2nd parameter and a type that the function pointer is dependent on as it's first.
i.e.
template <typename P, typename void(*fn)(P)>
auto function(P) -> otherType<P, fn>;
I want to make it so that I can just specify the function pointer in the template list without having to specify the dependent type as that type should somehow be able to be inferred from the function pointer that I specify (or maybe even the parameter list, but I think that it probably is too far down the line).
My first thought was to defer the conversion to a template parameter value, by passing a template typename and then convert to a value after the fact though template metaprogramming wizardry.
i.e.
template <typename F, typename P>
auto function(P) -> [[ something here to get otherType<P, fn> if F was a function pointer ]]
However, I'm not sure how I can do this. Any ideas?
Edit
What I'm trying to accomplish here is to make a helper function that will generate a class object. So, given what was said by StenSoft, this is what I've come up with. Unfortunately it doesn't work with a failure inside the main() function where it cannot match to the correct function due to deduction failure:
#include <iostream>
#include <functional>
template<typename T, typename F>
struct wrapper_fntor
{
T m_t;
F m_f;
wrapper_fntor(T t, F f) : m_t(t), m_f(f) {}
void invoke() { m_f(m_t); }
};
template<typename T, void(*fn)(T)>
struct wrapper_fn
{
T m_t;
wrapper_fn(T t) : m_t(t) {}
void invoke() { fn(m_t); }
};
template <typename T>
struct Wrapper;
template <typename Ret, typename P>
struct Wrapper<Ret(P)>
{
template <Ret(*fn)(P)>
static Ret function(P p)
{
return fn(std::forward<P>(p));
}
template <Ret(*fn)(P)>
static P get_param_type(P);
typedef decltype(get_param_type<Ret(P)>()) param_t;
};
template<typename F>
wrapper_fn<typename Wrapper<F>::param_t, &Wrapper<F>::function> make_wrapper(typename Wrapper<F>::param_t param)
{
return wrapper_fn<typename Wrapper<F>::param_t, &Wrapper<F>::function>(param);
}
template<typename F>
wrapper_fntor<typename Wrapper<F>::param_t, F> make_wrapper(typename Wrapper<F>::param_t param, F fntor)
{
return wrapper_fntor<typename Wrapper<F>::param_t, F>(param, fntor);
}
void function(int value)
{
std::cout << "function called " << value << std::endl;
}
int main()
{
auto x = make_wrapper<function>(3);
x.invoke();
}
demo
For a similar problem I have used a templated function inside a templated wrapper class and a macro (this actually works with any parameters and return type):
template <typename T>
struct Wrapper;
template <typename Ret, typename... Params>
struct Wrapper<Ret(Params...)>
{
template <Ret(*fn)(Params...)>
static Ret function(Params... params)
{
return fn(std::forward<Params>(params)...);
}
};
#define FUNCTION(fn) \
Wrapper<decltype(fn)>::function<fn>
I would like to use the SFINAE pattern to execute some code if I can instantiate a certain template class. Let's imagine this:
//Only instantiable with types T for which T.x() is ok:
template <class T>
class TemplateClass
{
T t;
public:
void foo() {
t.x();
}
}
template <class T>
class User
{
void foo()
{
"if TemplateClass<T> is ok then do something else do nothing"
}
}
How could I do that?
Thanks a lot!
EDIT: Based on edA-qa mort-ora-y's answer I tried:
template <class T>
struct TemplateClass
{
T t;
void foo() { t.x(); }
static const bool value = true;
};
struct A {};
struct B { void x() {} };
template <class T>
struct User
{
template<typename M>
typename boost::enable_if<TemplateClass<M> >::type func( )
{
std::cout << "enabled\n";
}
template<typename M>
typename boost::disable_if<TemplateClass<M> >::type func( )
{
std::cout << "disabled\n";
}
void foo()
{
func<TemplateClass<T> >();
}
};
User<A> a;
a.foo();
User<B> b;
b.foo();
But this returns "enabled enabled". What am I missing?
You should look at the boost boost/utility/enable_if.hpp header and the associated meta/template programming code.
The easiest way here is to have two versions of the foo function, both template functions. One of the functions will use the enable_if construct and the other will use the disable_if construct.
I'm sure you can find better examples at the boost website, but something like this:
template<typename M>
typename boost::enable_if<Template<M>>::type func( ) { }
This function will only be defined if Template is a valid type. Since you always want to compile you'll need the counterpart, the function to call when it is not valid:
template<typename M>
typename boost::disable_if<Template<M>>::type func( ) { }
I'm not sure you can, within a single template, define two member functions in this pattern without making them both template functions. I suppose you might be able to define the two template functions and set the default template parameter to T.
I hope that helps a bit.