How to call a specific method only if class has one? - c++

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

Related

How can I get the type of underlying data in a SFINAE template definition?

Say I have a library function int foo( const T& )
that can operate with some specific containers as argument:
std::vector<A> c1;
std::list<B>() c2;
auto a1 = foo(c1); // ok
auto a2 = foo(c2); // ok too
std::map<int, float> c3;
auto a3 = foo( c3 ); // this must fail
First, I wrote a traits class defining the allowed containers:
template <typename T> struct IsContainer : std::false_type { };
template <typename T,std::size_t N> struct IsContainer<std::array<T,N>> : std::true_type { };
template <typename... Ts> struct IsContainer<std::vector<Ts...>>: std::true_type { };
template <typename... Ts> struct IsContainer<std::list<Ts... >>: std::true_type { };
now, "Sfinae" the function:
template<
typename U,
typename std::enable_if<IsContainer<U>::value, U>::type* = nullptr
>
auto foo( const U& data )
{
// ... call some other function
return bar(data);
}
Works fine.
side note: bar() is actually inside a private namespace and is also called from other code.
On the contrary, foo() is part of the API.
But now I want to change the behavior depending on the types inside the container
i.e. inside foo():
call bar1(data) if argument is std::vector<A> or std::list<A> and
call bar2(data) if argument is std::vector<B> or std::list<B>
So I have this:
struct A {}; // dummy here, but concrete type in my case
struct B {};
template<
typename U,
typename std::enable_if<
( IsContainer<U>::value, U>::type* = nullptr && std::is_same<U::value_type,A> )
>
auto foo( const U& data )
{
// ... call some other function
return bar1(data);
}
template<
typename U,
typename std::enable_if<
( IsContainer<U>::value, U>::type* = nullptr && std::is_same<U::value_type,B> )
>
auto foo( const U& data )
{
// ... call some other function
return bar2(data);
}
With bar1() and bar2() (dummy) defined as:
template<typename T>
int bar1( const T& t )
{
return 42;
}
template<typename T>
int bar2( const T& t )
{
return 43;
}
This works fine, as demonstrated here
Now my real problem: the types A and B are actually templated by some underlying type:
template<typename T>
struct A
{
T data;
}
template<typename T>
struct B
{
T data;
}
And I want to be able to build this:
int main()
{
std::vector<A<int>> a;
std::list<B<float>> b;
std::cout << foo(a) << '\n'; // print 42
std::cout << foo(b) << '\n'; // print 43
}
My problem is: I don't know how to "extract" the contained type:
I tried this:
template<
typename U,
typename F,
typename std::enable_if<
( IsContainer<U>::value && std::is_same<typename U::value_type,A<F>>::value ),U
>::type* = nullptr
>
auto foo( const U& data )
{
return bar1(data);
}
template<
typename U,
typename F,
typename std::enable_if<
( IsContainer<U>::value && std::is_same<typename U::value_type,B<F>>::value ), U
>::type* = nullptr
>
auto foo( const U& data )
{
return bar2(data);
}
But this fails to build:
template argument deduction/substitution failed:
main.cpp:63:21: note: couldn't deduce template parameter 'F'
(see live here )
Q: How can I make this work?
Side note: please only C++14 (or 17) if possible, I would rather avoid C++20 at present.
There is a simple solution: define a trait IsA in exactly the same way you defined IsContainer:
template<class> struct IsA : std::false_type {};
template<class T> struct IsA<A<T>> : std::true_type {};
and then write
IsContainer<U>::value && IsA<typename U::value_type>::value
Depending on your exact use case, you might even not need the first trait, because for U = std::map<...>, IsA<typename U::value_type>::value will be false anyway.
You can define traits for isA/isB:
template <typename T> struct IsA : std::false_type { };
template <typename T> struct IsA<A<T>> : std::true_type { };
template <typename T> struct IsB : std::false_type { };
template <typename T> struct IsB<B<T>> : std::true_type { };
and SFINAE based on those traits.
Alternative might be tag dispatching:
template<typename T> int bar1( const T& t ) { return 42; }
template<typename T> int bar2( const T& t ) { return 43; }
template <typename T> struct Tag{};
template<typename T, typename U>
int bar(const T& t, Tag<A<T>>) { return bar1(t); }
template<typename T, typename U>
int bar(const T& t, Tag<B<U>>) { return bar2(t); }
template<
typename T,
typename std::enable_if<IsContainer<T>::value>::type* = nullptr
>
auto foo( const U& data ) -> decltype(bar(data, Tag<typename T::value_type>{}))
{
// ... call some other function
return bar(data, Tag<typename T::value_type>{});
}

Structural tuple-like / Wrapping a variadic set of template parameters values in a struct

I find myself wanting/needing to use a variadic composite type as a template parameter. Unfortunately, std::tuple<> is not a structural type, which makes the obvious approach a no-go:
#include <tuple>
template<typename... Ts>
struct composite {
std::tuple<Ts...> operands;
};
template<auto v>
void foo() {}
int main() {
constexpr composite tmp{std::make_tuple(1,2,3)};
foo<tmp>(); // <----- Nope!
}
Is there a reasonable way to build such a composite in a manner that works as a structural type?
Since the MCVE in isolation is trivially solvable as "just make foo() a variadic template", here's a more representative example:
On godbolt
#include <concepts>
#include <tuple>
template <typename T, template <typename...> typename U>
concept TemplatedConfig = requires(T x) {
{ U(x) } -> std::same_as<T>;
// A few more things identifying T as a valid config
};
template<auto Config>
struct proc;
// Basic
struct Basic {};
template<Basic v>
struct proc<v> {
constexpr int foo() { return 0; }
};
// Annotated
template<typename T>
struct Annotated {
T v;
int annotation;
};
template<TemplatedConfig<Annotated> auto v>
struct proc<v> {
constexpr int foo() { return 1; }
};
// ... more config / specialization pairs ...
// Composite
template<typename... Parts>
struct Composite {
std::tuple<Parts...> parts;
};
template<TemplatedConfig<Composite> auto v>
struct proc<v> {
constexpr int foo() { return 2; }
};
int main() {
constexpr Basic a = Basic{};
constexpr Annotated b{a, 12};
constexpr Composite c{std::make_tuple(a, b)};
static_assert(proc<a>{}.foo() == 0);
static_assert(proc<b>{}.foo() == 1);
static_assert(proc<c>{}.foo() == 2); <----- :(
}
Edit: If anyone is curious what a (almost) fully-realized tuple class based on the accepted answer looks like: https://gcc.godbolt.org/z/ThaGjbo67
Create your own structural tuple-like class?
Something like:
template <std::size_t I, typename T>
struct tuple_leaf
{
T data;
};
template <typename T> struct tag{ using type = T; };
template <typename Seq, typename...>
struct tuple_impl;
template <std::size_t... Is, typename... Ts>
struct tuple_impl<std::index_sequence<Is...>, Ts...> : tuple_leaf<Is, Ts>...
{
constexpr tuple_impl(Ts... args) : tuple_leaf<Is, Ts>{args}... {}
};
template <typename T, std::size_t I> constexpr const T& get(const tuple_leaf<I, T>& t) { return t.data; }
template <typename T, std::size_t I> constexpr T& get(tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T> constexpr const T& get(const tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T> constexpr T& get(tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T>
tag<T> tuple_element_tag(const tuple_leaf<I, T>&);
template <std::size_t I, typename Tuple>
using tuple_element = decltype(tuple_element_tag<I>(std::declval<Tuple>()));
template <std::size_t I, typename Tuple>
using tuple_element_t = typename tuple_element<I, Tuple>::type;
template <typename ... Ts>
using tuple = tuple_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
Demo

Is it possible to extract parameter types from a template <auto MEMFN>?

Is it possible to create a standalone template function which has a template parameter auto MEMFN (a member function pointer), and has the same return and parameter types as MEMFN has?
So, if MEMFN's type is
RETURN (OBJECT::*)(PARAMETERS...)
then the desired function is this:
template <auto MEMFN>
RETURN foo(OBJECT &, PARAMETERS...);
My problem is how to extract PARAMETERS... from MEMFN's type (RETURN and OBJECT are easy to do).
So I can call this function like this:
Object o;
foo<&Object::func>(o, <parameters>...);
As for request from n.m., here is a stripped down example from an actual code:
#include <utility>
template <typename RETURN, typename OBJECT, typename ...PARAMETERS>
struct Wrapper {
template <RETURN (OBJECT::*MEMFN)(PARAMETERS...)>
RETURN foo(PARAMETERS... parameters) {
// do whatever with MEMFN, parameters, etc. here, not part of the problem
}
};
struct Object {
template <auto MEMFN, typename RETURN, typename OBJECT, typename ...PARAMETERS>
RETURN call(OBJECT &&object, PARAMETERS &&...parameters) {
// here, MEMFN parameters and PARAMETERS must be the same
// Wrapper actually not created here, it is accessed by other means
Wrapper<RETURN, typename std::decay<OBJECT>::type, PARAMETERS...> w;
return w.template foo<MEMFN>(std::forward<PARAMETERS>(parameters)...);
}
};
struct Foo {
void fn(int);
};
int main() {
Object o;
Foo f;
o.call<&Foo::fn, void, Foo &, int>(f, 42);
// this is wanted instead:
// o.call<&Foo::fn>(f, 42);
}
Yes we can:
template <auto MemFn>
struct fooHelper;
template <typename Ret, typename Obj, typename ... Args, Ret (Obj::*MemFn)(Args...)>
struct fooHelper<MemFn>
{
static Ret call(Obj& obj, Args... args) {
return (obj.*MemFn)(args...);
}
};
template <auto MemFn, typename ... Args>
auto foo(Args ... args)
{
return fooHelper<MemFn>::call(args...);
}
Another way to define foo which doesn't introduce a brand new parameter pack is:
template <auto MemFn>
auto& foo = fooHelper<MemFn>::call;
Example usage:
#include <iostream>
struct moo
{
int doit (int x, int y) { return x + y; }
};
int main()
{
moo m;
std::cout << foo<&moo::doit>(m, 1, 2) << "\n";
}
(Perfect forwarding omitted for simplicity)
If you relax your demand on being standalone you can do something like:
#include <iostream>
template <auto MEMFN, class = decltype(MEMFN)>
struct S;
template <auto MEMFN, class Ret, class T, class... Args>
struct S<MEMFN, Ret (T::*)(Args...)> {
static Ret foo(T &o, Args... args) {
(o.*MEMFN)(args...);
}
};
struct A {
void foo(int a, int b) {
std::cout << a << " " << b << std::endl;
}
};
int main() {
A a;
S<&A::foo>::foo(a, 1, 2);
}
[live demo]
If no then you gonna have to have a patience to create a function overloads for each possible number of parameters:
#include <type_traits>
#include <tuple>
#include <iostream>
template <class, std::size_t>
struct DeduceParam;
template <class Ret, class T, class... Args, std::size_t N>
struct DeduceParam<Ret (T::*)(Args...), N> {
using type = std::tuple_element_t<N, std::tuple<Args...>>;
};
template <class>
struct DeduceResultAndType;
template <class Ret, class T, class... Args>
struct DeduceResultAndType<Ret (T::*)(Args...)> {
using result = Ret;
using type = T;
static constexpr decltype(sizeof(T)) size = sizeof...(Args);
};
template <auto MEMFN, class DRAT = DeduceResultAndType<decltype(MEMFN)>, std::enable_if_t<DRAT::size == 1>* = nullptr>
typename DRAT::result foo(typename DRAT::type o, typename DeduceParam<decltype(MEMFN), 0>::type param1) {
}
template <auto MEMFN, class DRAT = DeduceResultAndType<decltype(MEMFN)>, std::enable_if_t<DRAT::size == 2>* = nullptr>
typename DRAT::result foo(typename DRAT::type o, typename DeduceParam<decltype(MEMFN), 0>::type param1,
typename DeduceParam<decltype(MEMFN), 1>::type param2) {
}
struct A {
void foo(int a, int b) {
std::cout << a << " " << b << std::endl;
}
};
int main() {
A a;
foo<&A::foo>(a, 1, 2);
}

C++ template specialization - avoid redefinition

I want to have a generic function (or method) that accepts arguments of different types. If the provided type has 'one' method, the function should use it. If it has 'two' method, the function should use it instead.
Here's the invalid code:
#include <iostream>
template<typename Type> void func(Type t)
{
t.one();
}
template<typename Type> void func(Type t) // redefinition!
{
t.two();
}
class One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
class Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
Is it possible to achieve using SFINAE? Is it possible to achieve using type_traits?
Clarification:
I would be more happy if this would be possible using SFINAE. The best-case scenario is: use first template, if it fails use the second one.
Checking for method existence is only an example. What I really want is also checking for compatibility with other classes.
The task could be rephrased:
If the class supports the first interface, use it.
If the first interface fails, use the second interface.
If both fail, report an error.
Yes, it's possible. In C++11 an onward it's even relatively easy.
#include <iostream>
#include <type_traits>
template<class, typename = void>
struct func_dispatch_tag :
std::integral_constant<int, 0> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::one), void (C::*)() const>::value>
> : std::integral_constant<int, 1> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::two), void (C::*)() const>::value>
> : std::integral_constant<int, 2> {};
template<class C>
void func(C const&, std::integral_constant<int, 0>) {
std::cout << "fallback!\n";
}
template<class C>
void func(C const &c, std::integral_constant<int, 1>) {
c.one();
}
template<class C>
void func(C const &c, std::integral_constant<int, 2>) {
c.two();
}
template<class C>
void func(C const &c) {
func(c, func_dispatch_tag<C>{});
}
struct One
{
void one(void) const
{
std::cout << "one\n";
}
};
struct Two
{
void two(void) const
{
std::cout << "two\n";
}
};
struct Three {};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
func(Three());
return 0;
}
Important points:
We SFINAE on the second parameter of func_dispatch_tag. The compiler looks at all the template specializations which result in the parameters <C, void>. Since any of the latter is "more specialized" when SF doesn't occur (i.e when std::enable_if_t is void), it gets chosen.
The chosen specialization of the trait defines a tag which we do a tag dispatch on. Tag dispatch depends on function overloading, instead of function template specialization (that cannot be partially specialized).
You can define a fallback function (like I did), or static_assert. The number of tags we can define is limited only by the range of an int, so extending to other members is just a matter of adding another func_dispatch_tag specialization.
The member must be accessible, or SF will occur. Also, a class that has both members will result in ambiguity. Bear that in mind.
Here's another way. There's a little more boilerplate, but in the actual expression of the different implementations of func() it could be argued that the 'list of tests that passed' is more expressive.
Food for thought anyway.
Code is c++11. c++14 and 17 would be more succinct.
#include <iostream>
#include <type_traits>
#include <tuple>
// boilerplate required prior to c++17
namespace notstd {
using namespace std;
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
}
// test for having member function one()
template<class T, class Enable = notstd::void_t<>> struct has_one : std::false_type {};
template<class T> struct has_one<T, notstd::void_t<decltype(std::declval<T>().one())>> : std::true_type {};
//test for having member function two()
template<class T, class Enable = notstd::void_t<>> struct has_two : std::false_type {};
template<class T> struct has_two<T, notstd::void_t<decltype(std::declval<T>().two())>> : std::true_type {};
// a type collection of tests that pass
template<template <class...> class...Tests> struct passes_tests {
};
// meta-function to append a type
template<class Existing, template <class...> class Additional> struct append_pass;
template< template <class...> class...Tests, template <class...> class Additional>
struct append_pass<passes_tests<Tests...>, Additional> {
using type = passes_tests<Tests..., Additional>;
};
//
// meta-functions to compute a list of types of test that pass
//
namespace detail
{
template<class Previous, class T, template<class...> class Test, template<class...> class...Rest>
struct which_tests_pass_impl
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = typename which_tests_pass_impl<this_term, T, Rest...>::type;
};
template<class Previous, class T, template<class...> class Test>
struct which_tests_pass_impl<Previous, T, Test>
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = this_term;
};
}
template<class Type, template<class...> class...Tests> struct which_tests_pass
{
using type = typename detail::which_tests_pass_impl<passes_tests<>, Type, Tests...>::type;
};
//
// various implementations of func()
//
namespace detail
{
template<class T>
void func(T t, passes_tests<has_one>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_one, has_two>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_two>)
{
t.two();
}
template<class T>
void func(T t, passes_tests<>)
{
// do nothing
}
}
template<class T>
void func(T t)
{
detail::func(t, typename which_tests_pass<T, has_one, has_two>::type());
}
//
// some types
//
struct One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
struct Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
// test
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
The code below
handles member function constness correctly
is agnostic of the functions return types
prints a comprehensive error on failure
It could be even shorter with C++14, where you don't have to specify the return types of implemented functions and have templated variable declarations. If you want to handle rvalue overloads correctly, you need to provide another overload to as_memfun.
If testing for member functions alone is not enough, there is another approach in the last section, which offers far better customization options but is also lengthier to setup.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false : std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) ())
-> R (T::*) ()
{ return arg; }
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) () const)
-> R (T::*) () const
{ return arg; }
template<typename T> constexpr auto check_has_two(int)
-> decltype(as_memfun(&T::two))
{ return as_memfun(&T::two); }
template<typename T> constexpr auto check_has_two(...)
-> HasNone<T>;
template<typename T> constexpr auto check_has_one(int)
-> decltype(as_memfun(&T::one))
{ return as_memfun(&T::one); }
template<typename T> constexpr auto check_has_one(...)
-> decltype(check_has_two<T>(0))
{ return check_has_two<T>(0); }
template<typename T>
struct res { constexpr static auto detail = check_has_one<T>(0); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::res<T>::detail)()) {
return (t.*detail::res<T>::detail)();
}
And here are some test you would probably like to have
struct One {
void one();
};
struct Two {
void two();
};
struct TestBoth {
char one() const;
void two();
};
struct TestWilderStuff {
int one;
void two() const;
};
int main() {
func(One{});
func(Two{});
func(TestBoth{});
static_assert(decltype(func(TestBoth{})){} == 0, "Failed function selection");
func(TestWilderStuff{});
}
Since you seem to have more extensive constructions in mind than just testing for member function existence, here is the beginning of a vastly more powerful mechanism. You can use it as a drop-in replacement for the above solution and although it is far lengthier, it offers more customization and the possibility to do elaborate tests on your types in every step of the way.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false :
std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
// Generic meta templates used below
namespace Generics {
template<typename Getter, typename Else>
struct ChainGetter {
template<typename T> constexpr static auto get_(int)
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto get_(...)
-> decltype(Else::template get<T>())
{ return Else::template get<T>(); }
template<typename T> constexpr static auto get()
-> decltype(get_<T>(0))
{ return get_<T>(0); }
};
template<typename Getter, typename Test>
struct TestGetter {
template<typename T, typename R> using _type = R;
template<typename T> constexpr static auto get_()
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto test()
-> decltype(Test::template test<T>(get_<T>()));
template<typename T> constexpr static auto get()
-> _type<decltype(test<T>()),
decltype(get_<T>())
>
{ return get_<T>(); }
};
template<template<typename> class F>
struct FailGetter {
template<typename T>
constexpr static auto get() -> F<T>;
};
}
// Test only exists for member function pointer arguments
struct IsMemberFunctionTest {
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) ());
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) () const);
};
// Get member pointer to T::one
struct GetOne {
template<typename T>
constexpr static auto get() -> decltype(&T::one) { return &T::one; }
};
// Get member pointer to T::two
struct GetTwo {
template<typename T>
constexpr static auto get() -> decltype(&T::two) { return &T::two; }
};
using namespace Generics;
using getter_fail = FailGetter<HasNone>;
using get_two_tested = TestGetter<GetTwo, IsMemberFunctionTest>;
using getter_two = ChainGetter<get_two_tested, getter_fail>;
using get_one_tested = TestGetter<GetOne, IsMemberFunctionTest>;
using getter_one = ChainGetter<get_one_tested, getter_two>;
template<typename T>
struct result { constexpr static auto value = getter_one::template get<T>(); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::result<T>::value)()) {
return (t.*detail::result<T>::value)();
}

How to get element from std::tuple by type

I have a set of classes A, B, C and I want to have access instances of them from generic code by type, f.e
template<typename T>
newObject()
{
return m_storage->getNew();
}
where m_storage is instance of A or B or C, depends on T.
So I came up with std::tuple, but there is the problem because I can't get element from tuple by type.
std::tuple<A,B,C> m_tpl;
template<typename T>
newObject()
{
return m_tpl.get<T>().getNew();
}
Is there any way to do it?Is this possible?
Thanks.
PS:
I don't want to write the specialisation of newObject for each type.:-)
This is a draft from C++14 about getting value from tuple by type.
But before C++14 will come, you could write something like below:
namespace detail
{
template <class T, std::size_t N, class... Args>
struct get_number_of_element_from_tuple_by_type_impl
{
static constexpr auto value = N;
};
template <class T, std::size_t N, class... Args>
struct get_number_of_element_from_tuple_by_type_impl<T, N, T, Args...>
{
static constexpr auto value = N;
};
template <class T, std::size_t N, class U, class... Args>
struct get_number_of_element_from_tuple_by_type_impl<T, N, U, Args...>
{
static constexpr auto value = get_number_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
};
} // namespace detail
template <class T, class... Args>
T get_element_by_type(const std::tuple<Args...>& t)
{
return std::get<detail::get_number_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t);
}
int main()
{
int a = 42;
auto t = std::make_tuple(3.14, "Hey!", std::ref(a));
get_element_by_type<int&>(t) = 43;
std::cout << a << std::endl;
// get_element_by_type<char>(t); // tuple_element index out of range
return 0;
}
A simple variadic mixin container does the trick:
template < typename T > struct type_tuple_value
{
T value;
type_tuple_value ( T&& arg ) : value(std::forward<T>(arg)) {}
};
template < typename ...T > struct type_tuple : type_tuple_value<T>...
{
template < typename ...Args > type_tuple ( Args&&... args ) :
type_tuple_value<T>(std::forward<T>(args))... {}
template < typename U > U& get() { return type_tuple_value<U>::value; }
template < typename U > const U& get() const { return type_tuple_value<U>::value; }
};
Example
You can also compute the position of the type with a constexpr function if you don't like template.
constexpr int count_first_falses() { return 0; }
template <typename... B>
constexpr int count_first_falses(bool b1, B... b)
{
if (b1) return 0;
else return 1 + count_first_falses(b...);
}
template <typename E, typename... T>
decltype(auto) tuple_get_by_type(const std::tuple<T...>& tuple)
{
return std::get<count_first_falses((std::is_same<T, E>::value)...)>(tuple);
}