How can I specialized a template function to return true if objects belong to certain templates/classes and false for other templates/classes
no classes have virtual functions.
I have a template:
template<typename t1, typename t2, bool a> struct DynamicData{...}
which I want to do special processing, or any class derived from that template.
in the following example I have a specialization of DynamicData<>: DynamicDataSym<> for which I did a specialization of is_dynamic<>, but because DynamicDataSym<> is derived from DynamicData<> I wonder if there is a way that anytime I create a new derive class from DynamicData<> I can have is_dynamic<> work automatically.
the design with the templates cannot be changed, and no virtual methods are allowed!
Also working with C++17, so c++20 & C++23 out of the equation!
class ClrngAssetBuff
{
int name1 {};
public:
int GetName();
};
template<typename t1, typename t2, bool a>
struct DynamicData
{
t1 m1;
t2 m2;
};
template<typename t1, typename t2, int sym>
struct DynamicDataSym : DynamicData<t1, t2, true>
{
int some() { return sym; }
};
template<typename t1>
struct DynamicDataSym2 : DynamicData<t1, float, true>
{
float some() { return 1.0f; }
};
template<typename T>
constexpr bool is_dynamic(const T& t)
{
return false;
}
template<typename T1, typename T2, bool a>
constexpr bool is_dynamic(const DynamicData<T1, T2, a>& t)
{
return true;
}
template<typename T1, typename T2, int a>
constexpr bool is_dynamic(const DynamicDataSym<T1, T2, a>& t)
{
return true;
}
template<typename T>
constexpr bool is_dynamic2()
{
return is_dynamic<>(T {});
}
template<typename T> void DoDynamicInfo(const T& t);
template<typename T> void ExecuteMessage(const T& t);
template<typename T>
void ProcessMessage(const T& t)
{
if (is_dynamic2<T>())
DoDynamicInfo(t);
ExecuteMessage(t);
}
int main(int argc, char** argv)
{
ClrngAssetBuff msg1;
ProcessMessage(msg1);
DynamicData<int, double, false> msg2;
ProcessMessage(msg2);
DynamicDataSym<int, int, 10> msg3;
ProcessMessage(msg3);
DynamicDataSym2<long long> msg4;
ProcessMessage(msg4); // oops should call DoDynamicInfo(msg4)
}
If I correctly understand what do you want... you're looking for something as follows
template <typename T1, typename T2, bool B>
std::true_type is_dynamic_helper (DynamicData<T1, T2, B> *);
std::false_type is_dynamic_helper (...);
template <typename T>
constexpr auto is_dynamic
= decltype(is_dynamic_helper(std::declval<T*>()))::value;
that is a template variable (available starting from C++14, so is good for a C++17 solution) that is true when a pointer of the template T type can be converted to a DynamicData<T1, T2, B> (for generics T1, T2 types and a generic boolean B value), false otherwise.
So is_dynamic is true when T is a DynamicData or derive from a DynamicData. Unfortunately this doesn't works when T derive from two or more DynamicData classes.
Observe that I've made is_dynamic a constexpr variable, so you can use is constexpr, if you want, inside ProcessMessage()
template <typename T>
void ProcessMessage(const T& t)
{
if constexpr ( is_dynamic<T> )
DoDynamicInfo(t);
ExecuteMessage(t);
}
The following is a full compiling C++17 example
#include <iostream>
class ClrngAssetBuff
{
int name1 {};
public:
int GetName();
};
template <typename t1, typename t2, bool a>
struct DynamicData
{ t1 m1; t2 m2; };
template <typename t1, typename t2, int sym>
struct DynamicDataSym : DynamicData<t1, t2, true>
{ int some() { return sym; } };
template <typename t1>
struct DynamicDataSym2 : DynamicData<t1, float, true>
{ float some() { return 1.0f; } };
template <typename T>
void DoDynamicInfo(const T& t)
{ std::cout << "Do Dynamic Info" << std::endl; }
template <typename T>
void ExecuteMessage(const T& t)
{ std::cout << "Execute Message" << std::endl; }
template <typename T1, typename T2, bool B>
std::true_type is_dynamic_helper (DynamicData<T1, T2, B> *);
std::false_type is_dynamic_helper (...);
template <typename T>
constexpr auto is_dynamic
= decltype(is_dynamic_helper(std::declval<T*>()))::value;
template <typename T>
void ProcessMessage(const T& t)
{
if constexpr ( is_dynamic<T> )
DoDynamicInfo(t);
ExecuteMessage(t);
}
int main()
{
ClrngAssetBuff msg1;
std::cout << "---- msg1" << std::endl;
ProcessMessage(msg1);
DynamicData<int, double, false> msg2;
std::cout << "---- msg2" << std::endl;
ProcessMessage(msg2);
DynamicDataSym<int, int, 10> msg3;
std::cout << "---- msg3" << std::endl;
ProcessMessage(msg3);
DynamicDataSym2<long long> msg4;
std::cout << "---- msg4" << std::endl;
ProcessMessage(msg4); // now call DoDynamicInfo(msg4)
}
Related
I have the following code which compiles nicely:
#include <iostream>
struct Res {};
struct Jac {};
template <typename T, typename S>
class A;
template <typename S>
class A<Res, S>
{
public:
A() { std::cout << "A<Res, S>" << std::endl; }
};
template <typename S>
class A<Jac, S>
{
public:
A() { std::cout << "A<Jac, S>" << std::endl; }
};
template <typename T, typename S>
class B;
template <typename S>
class B<Res, S>
{
public:
B() { std::cout << "B<Res, S>" << std::endl; }
};
template <typename S>
class B<Jac, S>
{
public:
B() { std::cout << "B<Jac, S>" << std::endl; }
};
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
int main() {
foo<int, A<Res,int>>();
foo<int, B<Res,int>>();
return 0;
}
However I am not happy with the calls inside my main() function. I would like them to look like this:
foo<int, A>();
foo<int, B>();
which would imply the following modification of the templates for foo():
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
This obviously does not compile. The idea is to have a function, which would instantiate either A or B without explicitly specifying T for my classes because I know that foo() has to create 2 instances with Res and Jac as type parameters. Is there any way to make the code neater and achieve such a behavior?
You can change foo to accept a template template parameter CT that is templated on two types, and enable_if the specific overload based on whether CT<Res, S> is the same type as A<Res, S>, or B<Res, S>:
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res,S>, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res, S>, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
Here's a demo.
Currently I'm looking for some way to get the parameter types of an overloaded member function by argument count and instance type.
In my case we know the name, the return type (void) and the argument count.
In addition there can't be a second overload with the same argument count and the functions are never cv nor ref qualified.
A sample:
#include <tuple>
#include <type_traits>
#include <iostream>
class TupleLikeType{
public:
void Deconstruct(int& a, char& b){
a = 12;
b = 'A';
}
void Deconstruct(int& a, char& b, bool& c){
Deconstruct(a, b);
c = true;
}
};
template<typename Type, std::size_t ArgCount>
class TupleDeconstructTypes{
public:
using type =
//to implement
std::conditional_t<ArgCount == 2,
std::tuple<int&, char&>, std::tuple<int&, char&, bool&>
>
//to implement end
;
};
template<typename T>
class DeconstructAdapter
{
private:
T& obj;
public:
template<
typename DepT = T
, typename... Args
>
constexpr void operator()(Args&&... args)
{
return obj.Deconstruct(std::forward<Args>(args)...);
}
public:
constexpr DeconstructAdapter(T& toDeconstruct) : obj(toDeconstruct)
{
}
};
template<typename Tuple>
class TupleWithOutRefs{};
template<template <typename...> typename Tuple, typename... T>
class TupleWithOutRefs<Tuple<T...>>{
public:
using type = Tuple<std::remove_reference_t<T>...>;
};
template<
std::size_t ArgCount
,typename T
,typename Args = typename TupleDeconstructTypes<T, ArgCount>::type
,typename TempTuple = typename TupleWithOutRefs<Args>::type
>
auto CreateTuple(T& t){
auto adapter = DeconstructAdapter(t);
TempTuple result{};
//std::apply calls std::invoke
//during this call all out-references are bound to our allocated stack memory
std::apply(adapter, result);
return result;
}
int main(){
//usage
static_assert(std::is_same_v<
TupleDeconstructTypes<TupleLikeType, 2>::type,
std::tuple<int&, char&>
>);
TupleLikeType t;
auto tuple = CreateTuple<2>(t);
std::cout << std::get<0>(tuple);
}
Do you have any idea to solve this in a generic way?
With limited number, you can do something like:
template <std::size_t Size>
struct helper;
template <>
struct helper<0>
{
template <typename C>
std::tuple<> operator() (void (C::*) ()) const;
};
template <>
struct helper<1>
{
template <typename C, typename T1>
std::tuple<T1> operator() (void (C::*) (T1)) const;
};
template <>
struct helper<2>
{
template <typename C, typename T1, typename T2>
std::tuple<T1, T2> operator() (void (C::*) (T1, T2)) const;
};
template <>
struct helper<3>
{
template <typename C, typename T1, typename T2, typename T3>
std::tuple<T1, T2, T3> operator() (void (C::*) (T1, T2, T3)) const;
};
// ...
template<typename Type, std::size_t ArgCount>
using TupleDeconstructTypes = decltype(helper<ArgCount>{}(&Type::Deconstruct));
Demo
I have a tuple of objects of different classes. I want to iterate over the tuple and call a certain method only if those class has one.
For example (pseudo-code):
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
int i = 0;
tuple<A, B, C> t;
for ( auto t_element : t )
{
if constexpr ( has_get_method( decltype(t_element) ) )
{
i += t_element.get( );
}
}
I already know how to iterate over the tuple and check if a class has some method using sfinae but how do I skip object that do not have the required method?
EDIT:
If you found this old question in the future, please know that this can be done much easier now using concepts from C++20 standard.
Just write a sfinae'd function and a catchall in case the previous one fails. You don't have to use if constexpr for that and you can't use it actually in C++14 (that is how you tagged the question).
Here is a minimal, working example:
#include <tuple>
#include <iostream>
auto value(...) { return 0; }
template <typename T>
auto value(T &t) -> decltype(t.get()) {
return t.get();
}
struct A { int get() { return 5; }; };
struct B {};
struct C { int get() { return 10; }; };
int main() {
int i = 0;
std::tuple<A, B, C> t;
i += value(std::get<0>(t));
i += value(std::get<1>(t));
i += value(std::get<2>(t));
std::cout << i << std::endl;
}
See it up and running on wandbox.
If you have any argument you want to use to test it, you can use std::forward as:
template <typename T, typename... Args>
auto value(T &t, Args&&... args)
-> decltype(t.get(std::forward<Args>(args)...))
{ return t.get(std::forward<Args>(args)...); }
Then invoke it as:
i += value(std::get<0>(t), params);
You can create a type-traits, to check if a class has a get() method, by declaring a couple of functions (no need to define them)
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
The following is a fully working c++17 example
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
template <typename T>
int addGet (T & t)
{
int ret { 0 };
if constexpr ( withGet<T>{} )
ret += t.get();
return ret;
}
int main ()
{
int i = 0;
std::tuple<A, B, C> t;
i += addGet(std::get<0>(t));
i += addGet(std::get<1>(t));
i += addGet(std::get<2>(t));
std::cout << i << std::endl;
}
If you can't use if constexpr, you can write (in c++11/14) getAdd() as follows, using tag dispatching
template <typename T>
int addGet (T & t, std::true_type const &)
{ return t.get(); }
template <typename T>
int addGet (T & t, std::false_type const &)
{ return 0; }
template <typename T>
int addGet (T & t)
{ return addGet(t, withGet<T>{}); }
--EDIT--
The OP ask
my code needs to check for a templated method with parameters. Is it possible to modify your solution so that instead of int get() it could check for something like template<class T, class U> void process(T& t, U& u)?
I suppose it's possible.
You can create a type-traits withProcess2 (where 2 is the number of arguments) that receive three template type parameters: the class and the two template types
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
The following is a fully working example (c++17, but now you know how to make it c++14) with A and C with a process() with 2 template parameters and B with a process() with only 1 template parameter.
#include <iostream>
#include <type_traits>
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
struct A
{
template <typename T, typename U>
void process(T const &, U const &)
{ std::cout << "A::process(T, U)" << std::endl; }
};
struct B
{
template <typename T>
void process(T const &)
{ std::cout << "B::process(T)" << std::endl; }
};
struct C
{
template <typename T, typename U>
void process(T &, U &)
{ std::cout << "C::process(T, U)" << std::endl; }
};
template <typename T>
void callProcess (T & t)
{
static int i0 { 0 };
static long l0 { 0L };
if constexpr ( withProcess2<T, int &, long &>{} )
t.process(i0, l0);
}
int main ()
{
std::tuple<A, B, C> t;
callProcess(std::get<0>(t)); // print A::process(T, U)
callProcess(std::get<1>(t)); // no print at all
callProcess(std::get<2>(t)); // print C::process(T, U)
}
Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.
class Foo
{
// For collections
template<class T>
typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
}
This won't compile.
error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if'
error: expected a type, got '! boost::is_same::value'
How about:
template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
And then
class Foo
{
// For collections
template<class T>
typename std::enable_if<is_std_vector<T>::value, const T&>::type
doSomething();
// For single types
template<class T>
typename std::enable_if<!is_std_vector<T>::value, const T&>::type
doSomething();
};
Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like
class Foo
{
// For collections
template<class T>
typename boost::enable_if<
typename boost::is_same<typename std::vector<typename T::value_type>, T>,
const T&>::type doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if_с<
!boost::is_same<typename std::vector<typename T::value_type>, T>::value,
const T&>::type doSomething()
{ }
}
Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.
What about a sort of tag dispatching?
#include <vector>
#include <iostream>
template <typename, typename>
struct isSame
{ typedef int type; };
template <typename T>
struct isSame<T, T>
{ typedef long type; };
struct foo
{
template <typename T>
T const & doSomething (T const & t, int)
{ std::cout << "int version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t, long)
{ std::cout << "long version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t)
{ return doSomething(t, typename isSame<
typename std::vector<typename T::value_type>, T>::type()); }
};
int main ()
{
foo f;
std::vector<int> v;
f.doSomething(v); // print "long version"
}
If what you want is to overload the function based on whether you are given a vector or not
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Foo {
public:
// For collections
template <class T>
const vector<T>& do_something(const std::vector<T>& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
// For single types
template <class T>
const T& do_something(const T& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
If you want to be even more general and check for any instantiated type
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace {
template <typename T, template <typename...> class TT>
struct IsInstantiationOf
: public std::integral_constant<bool, false> {};
template <template <typename...> class TT, typename... Args>
struct IsInstantiationOf<TT<Args...>, TT>
: public std::integral_constant<bool, true> {};
} // namespace anonymous
class Foo {
public:
// For collections
template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
std::decay_t<VectorType>, std::vector>::value>* = nullptr>
void do_something(VectorType&&) {
cout << "Vector overload" << endl;
}
// For single types
template <class T, typename std::enable_if_t<!IsInstantiationOf<
std::decay_t<T>, std::vector>::value>* = nullptr>
void do_something(T&&) {
cout << "Non vector overload" << endl;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675
Suppose we have:
template<typename F, typename T1, typename T2>
void my_magic_method(F func, T1 t1, T2 t2)
{
if (???)
func(t1);
else
func(t1,t2);
}
What can help me to determine:
Number of arguments
Maybe types of each argument
Type of return value
I cant use variadic templates because of MSVS 2010...
UPDATE
My first solution:
template<typename F>
auto my_magic_func(F f) -> decltype(f(1))
{
return f(1);
}
template<typename F>
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3))
{
return f(2,3);
}
int main()
{
auto x1 = my_magic_func([](int a){ return a+100; });
auto x2 = my_magic_func([](int a, int b){ return a*b; });
// x1 == 1+100
// x2 == 2*3
}
Thats my way of function-type overloading.
It works, but maybe a better solutuion?
Not exactly what you asked for, but if I understand your intent correctly, in VC++ 2010 it's possible (but ugly) via simple overloading based on arity:
#include <utility>
#include <string>
#include <iostream>
template<typename F, typename T1>
auto my_magic_method(F&& func, T1&& t1) ->
decltype(std::forward<F>(func)(std::forward<T1>(t1)))
{
return std::forward<F>(func)(std::forward<T1>(t1));
}
template<typename F, typename T1, typename T2>
auto my_magic_method(F&& func, T1&& t1, T2&& t2) ->
decltype(std::forward<F>(func)(std::forward<T1>(t1), std::forward<T2>(t2)))
{
return std::forward<F>(func)(std::forward<T1>(t1), std::forward<T2>(t2));
}
struct string_to_float_functor
{
float operator ()(std::string const& s) const
{
return std::stof(s);
}
};
int main()
{
auto a = my_magic_method([](std::string const& x) { return x + x; }, "foo");
auto b = my_magic_method([](double x, int y) { return x * y; }, 21.5, 3);
auto c = my_magic_method(string_to_float_functor(), "3.14159265");
std::cout << a << '\n' << b << '\n' << c << '\n';
}
This supports unary and binary functors – continue the pattern and add overloads for other arities as needed.
Here's a few approaches; all assume C++11. Tested on clang++ 3.2 with -std=c++11.
//Taking a function pointer argument (template types inferred)
template <typename ret, typename ... Args>
constexpr int arg_count(ret (*f)(Args...)) {
return sizeof...(Args);
}
//Taking a function type (or related) directly
template <typename T>
struct ArgCount {
static const int value = 0;
};
template <typename Ret, typename ... Args>
struct ArgCount<Ret(Args...)> {
static const int value = sizeof...(Args);
};
template <typename Ret, typename ... Args>
struct ArgCount<Ret(*)(Args...)> {
static const int value = sizeof...(Args);
};
template <typename Ret, typename ... Args>
struct ArgCount<Ret(&)(Args...)> {
static const int value = sizeof...(Args);
};
//Using the latter for dispatch
template <int N>
struct helper {
template<typename F, typename T1, typename T2>
static void call(F func, T1 t1, T2 t2);
};
template <>
struct helper<1> {
template<typename F, typename T1, typename T2>
static void call(F func, T1 t1, T2 t2) {
func(t1);
}
};
template <>
struct helper<2> {
template<typename F, typename T1, typename T2>
static void call(F func, T1 t1, T2 t2) {
func(t1, t2);
}
};
template<typename F, typename T1, typename T2>
void my_magic_method(F func, T1 t1, T2 t2)
{
helper<ArgCount<F>::value>::call(func, t1, t2);
}
//Testing
#include <cstdio>
void a(int a, int b) { printf("%i\n", a + b); }
void b(int x) { printf("%i\n", x); }
int main() {
printf("%i %i\n", arg_count(a), arg_count(b));
printf("%i %i\n", ArgCount<decltype(a)>::value, ArgCount<decltype(b)>::value);
my_magic_method(a, 1, 2);
my_magic_method(b, 1, 2);
}