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 don't think this is possible based on what I've read however I'm hoping someone here may know of some solution that would get this to work.
I have a vector (maths) class for C++
template <typename T, size_t N> class vec;
And want to create a varadic friend function apply to apply a function to these vectors element-wise
i.e.
template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&... args);
which is valid (untested yet)
however I want to achieve a pattern like
template <typename F> friend vec<typename std::result_of<F&&(T&&)>::type, N> apply(F&& f, const vec<T, N>& V);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<T, N>& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const T& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const T& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<U, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const U& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const vec<U, N>& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const U& V1, const vec<T, N>& V2);
note that only one of the arguments is required to be a vector any scalars would be broadcasted to the length of the vector.
The idea is that apply(pow, /*vec<float,N>*/V, /*int*/n) -> {pow(V.v[i],n)...} where i -> 0 ... N rather than apply(pow, /*vec<float,N>*/V, /*int*/n) -> apply(pow, /*vec<float,N>*/V, /*vec<int,N>*/tmp{/*int*/n}) {pow(V.v[i],tmp.v[i])...}
So I would like to be able to write something like the following (which isn't valid C++, but it should give an idea of what I want to achieve)
template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&||scalar<Args>::type... args) {
vec<typename std::result_of<pow(Args&&...)>::type, N> r;
for (int i= 0; i < N; i++) { r = f((is_vec<Args>?args.v[i]:args)...); }
return r;
}
EDIT:
Based on Frank's comments I'm looking for something along the lines of
template<typename F, typename ...Args, size_t N>
vec<typename std::enable_if<sum<is_vec<Args,N>...>::value > 0, std::result_of<F&&(base_type<Args>::type&&...)>::type>::type, N>
(F&& f, Args&&...args) {
vec<typename std::result_of<F&&(base_type<Args>::type&&...)>::type, N> result;
for(std::size_t i = 0 ; i < N ; ++i) { result.v[i] = f(extract_v(std::forward<Args>(args),i)...); }
return result;
}
however I'm unsure if this version could even compile as it may be too ambiguous to be able to detriment the value of N.
Not sure to understand what do you exactly want but...
It seems to me that can be useful a custom type traits to extract, from a list of types, the dimension of the Vec, iff (if and only if) in the list of types there is at least one Vec and there aren't Vec's of different lengths.
I suggest something as follows, heavily based on template specialization,
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;
// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
{ };
// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;
// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
{ };
with the help of a template static variable
template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };
Now should be easy.
You can write an apply() function that receive a variadic list of args of types Args... and is SFINAE enabled iff dimVecV<Args...> is defined
template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
{ return applyH1(std::make_index_sequence<N>{}, f, as...); }
Observe that the N variable is used to SFINAE enable/disable the function but is useful itself: it's used to pass a std::index_sequence from 0 to N-1 to the first helper function applyH1()
template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
{ return { applyH2<Is>(f, as...)... }; }
that initialize the returned Vec with single values calculated from the second helper function applyH2()
template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
{ return f(extrV<I>(as)...); }
that uses a set of template functions extrV()
template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
{ return v[I]; }
template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
{ return v; }
to extract the I-th element from a Vec or to pass-through a scalar value.
It's a little long but not particularly complicated.
The following is a full working example
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t N>
class Vec;
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;
// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
{ };
// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;
// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
{ };
template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };
template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
{ return v[I]; }
template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
{ return v; }
template <typename T, std::size_t N>
class Vec
{
private:
std::array<T, N> d;
public:
template <typename ... Ts>
Vec (Ts ... ts) : d{{ ts... }}
{ }
T & operator[] (int i)
{ return d[i]; }
T const & operator[] (int i) const
{ return d[i]; }
};
template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
{ return f(extrV<I>(as)...); }
template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
{ return { applyH2<Is>(f, as...)... }; }
template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
{ return applyH1(std::make_index_sequence<N>{}, f, as...); }
long foo (int a, int b)
{ return a + b + 42; }
int main ()
{
Vec<int, 3U> v3;
Vec<int, 2U> v2;
auto r1 { apply(foo, v2, v2) };
auto r2 { apply(foo, v3, v3) };
auto r3 { apply(foo, v3, 0) };
static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );
// apply(foo, v2, v3); // compilation error
// apply(foo, 1, 2); // compilation error
}
You can achieve what you want through a combination of partial template specialization and parameter pack extension.
#include <array>
template<typename T, std::size_t N>
using Vec = std::array<T, N>;
template<typename T>
struct extract {
static auto exec(T const& v, std::size_t) {return v;}
enum { size = 1 };
};
template<typename T, std::size_t N>
struct extract<Vec<T,N>> {
static auto exec(Vec<T,N> const& v, std::size_t i) {return v[i];}
enum {size = N};
};
template<typename T>
auto extract_v(T const& v, std::size_t i) {return extract<T>::exec(v, i);}
template<typename... args>
struct extract_size {
enum {size = 1};
};
template<typename first, typename... rest>
struct extract_size<first, rest...> {
enum {
rest_size_ = extract_size<rest...>::size,
self_size_ = extract<first>::size,
size = rest_size_ > self_size_ ? rest_size_ : self_size_
};
static_assert(self_size_ == 1 || rest_size_ == 1 || rest_size_ == self_size_, "");
};
template<typename F, typename... args_t>
auto apply(F const& cb, args_t&&... args) {
constexpr std::size_t size = extract_size<std::decay_t<args_t>...>::size;
using result_t = decltype(cb(extract_v(std::forward<args_t>(args),0)...));
Vec<result_t, size> result;
for(std::size_t i = 0 ; i < size ; ++i) {
result[i] = cb(extract_v(std::forward<args_t>(args),i)...);
}
return result;
}
I need to make a function take only the first value of every std::pair passed to its arguments. Values passed that are not of type std::pair will be used unaltered. My following solution only works for functions that takes two arguments. I need to know how to generalize this to any number of arguments passed.
#include <type_traits>
template <typename T, typename = void>
struct has_first_type : std::false_type { };
template <typename T>
struct has_first_type<T, std::void_t<typename T::first_type>> : std::true_type { };
template <typename R, typename T, typename U, typename = void> struct act_on_first;
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<has_first_type<T>::value && has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t.first, u.first);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<has_first_type<T>::value && !has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t.first, u);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<!has_first_type<T>::value && has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t, u.first);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<!has_first_type<T>::value && !has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t, u);
}
};
struct foo {
template <typename... Args>
std::size_t operator()(Args&&...) { return sizeof...(Args); } // Simple example only.
};
template <typename T, typename U>
std::size_t bar (const T& t, const U& u) {
return act_on_first<std::size_t, T, U>::execute(t, u, foo());
}
// Testing
#include <iostream>
int main() {
std::pair<int, bool> k = {3, true};
std::pair<int, char> m = {5, 't'};
std::cout << bar(k,m) << '\n'; // 2
std::cout << bar(k,5) << '\n'; // 2
std::cout << bar(3,m) << '\n'; // 2
std::cout << bar(3,5) << '\n'; // 2
}
Write a transformer, that either gives you .first for pairs or just returns its argument:
template <typename T> T const& take_first(T const& x) { return x; }
template <typename T, typename U>
T const& take_first(std::pair<T, U> const& p) { return p.first; }
template <typename... Args>
std::size_t bar(Args const&... args) {
return foo{}(take_first(args)...);
}
struct foo {
void bar(int&&) && { }
};
template<class T>
using bar_t = void (std::decay_t<T>::*)(int&&) /* add && if T is an rvalue reference */;
int main()
{
using other_t = void (foo::*)(int&&) &&;
static_assert(std::is_same<bar_t<foo&&>, other_t>::value, "not the same");
return 0;
}
I want that
bar_t<T> yields void (foo::*)(int&&) if T = foo
bar_t<T> yields void (foo::*)(int&&) const if T = foo const
bar_t<T> yields void (foo::*)(int&&) & if T = foo&
bar_t<T> yields void (foo::*)(int&&) const& if T = foo const&
and so on. How can I achieve that?
This should do the job:
template <typename, typename T> struct add {using type = T;};
template <typename F, typename C, typename R, typename... Args>
struct add<F const, R (C::*)(Args...)> {using type = R (C::*)(Args...) const;};
template <typename F, typename C, typename R, typename... Args>
struct add<F&, R (C::*)(Args...)> :
std::conditional<std::is_const<F>{}, R (C::*)(Args...) const&,
R (C::*)(Args...) &> {};
template <typename F, typename C, typename R, typename... Args>
struct add<F&&, R (C::*)(Args...)> :
std::conditional<std::is_const<F>{}, R (C::*)(Args...) const&&,
R (C::*)(Args...) &&> {};
Demo. Note that the underlying type of F is ignored.
I got class with template methods that looks at this:
struct undefined {};
template<typename T> struct is_undefined : mpl::false_ {};
template<> struct is_undefined<undefined> : mpl::true_ {};
template<class C>
struct foo {
template<class F, class V>
typename boost::disable_if<is_undefined<C> >::type
apply(const F &f, const V &variables) {
}
template<class F, class V>
typename boost::enable_if<is_undefined<C> >::type
apply(const F &f, const V &variables) {
}
};
apparently, both templates are instantiated, resulting in compile time error.
is instantiation of template methods different from instantiation of free functions?
I have fixed this differently, but I would like to know what is up.
the only thing I can think of that might cause this behavior, enabling condition does not depend immediate template arguments, but rather class template arguments
Thank you
Your C does not participate in deduction for apply. See this answer for a deeper explanation of why your code fails.
You can resolve it like this:
template<class C>
struct foo {
template<class F, class V>
void apply(const F &f, const V &variables) {
apply<F, V, C>(f, variables);
}
private:
template<class F, class V, class C1>
typename boost::disable_if<is_undefined<C1> >::type
apply(const F &f, const V &variables) {
}
template<class F, class V, class C1>
typename boost::enable_if<is_undefined<C1> >::type
apply(const F &f, const V &variables) {
}
};