I have multiple std::function. Each of them have different input and output, and the input of one std::function might be the output of another std::function, which means that "serial" convert from one to another.
Maybe I can't describe it clear enough. Let code talks
std::function<bool(double)> combine(std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return std::bind(convert1, convert2, convert3)//error. A function directly convert [double] to [bool] using convert1, convert2, convert3
}
Here is very simple code which I already remove the pointless code and show the core of my meaning.
So you can see convert1 do conversion from double to int, convert2 do conversion from int to char and convert3 do conversion from char to bool. Now I need to combine them together and so that I can directly convert double to bool.
And you know, I am not really want to convert double to bool. It's only for test.
One option to implement this is that write a help function:
bool helper(double d
, std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return convert3(convert2(convert1(d)));
}
std::function<double(bool)> combine(std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return helper;
}
But it's ugly code and maybe I use this conversion in a common way, which means that I should write this helper for all kind of my conversion.
So, is there a directly way to combine these function together?
you can use lambda expression to do this.
std::function<bool(double)> combine(std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return [=](double d){return convert3(convert2(convert1(d)));}
}
Or you can use lambda directly in your code and do not use this combine function at all, also it would be more clear what happened.
If you still want use combine function and want a more generic one, maybe you can try something like this. (just a simple example)
template<typename Converter>
auto combineX(Converter converter){
return converter;
}
template<typename Converter, typename ...Converters>
auto combineX(Converter converter, Converters... converters){
return [converter,remain = combineX(converters...)](auto x){return remain(converter(x));};
}
Creating a simple type traits to extract the input type of the last function
template <typename, typename...>
struct lastFnType;
template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
{ using type = typename lastFnType<F1, Fn...>::type; };
template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
{ using type = T1; };
you can transform the apple apple's solution (+1) in a more general variadic template recursive solution
template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
{ return conv; }
template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2,
Fn ... fn)
{
using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;
return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
}
But observe that the order of the converter is inverted (call with last used converter first; so combine(convert3, convert2, convert1))
The following is a full example
#include <functional>
template <typename, typename...>
struct lastFnType;
template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
{ using type = typename lastFnType<F1, Fn...>::type; };
template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
{ using type = T1; };
template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
{ return conv; }
template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2,
Fn ... fn)
{
using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;
return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
}
int fn1 (double d)
{ return d*2.0; }
char fn2 (int i)
{ return i+3; }
bool fn3 (char c)
{ return c == 'a'; }
int main ()
{
std::function<int(double)> f1 { fn1 };
std::function<char(int)> f2 { fn2 };
std::function<bool(char)> f3 { fn3 };
auto cmb = combine(f3, f2, f1);
bool b { cmb(3.2) };
}
You may do:
template <typename T, typename F>
decltype(auto) apply(T&& t, F&& f)
{
return std::forward<F>(f)(std::forward<T>(t));
}
template <typename T, typename F, typename... Fs>
decltype(auto) apply(T&& t, F&& f, Fs&&... fs)
{
return apply(std::forward<F>(f)(std::forward<T>(t)), std::forward<Fs>(fs)...);
}
with usage:
apply(4,
[](int i) { return i * 10; },
[](auto i) {return i + 2;},
[](auto n){ return n / 10.f; })
Demo
Related
What I'm trying to do is find a clean way to implement a concept for a callable object that takes in a single parameter of type either int or long.
My first attempt was to create a single concept with a secondary template parameter to ensure the parameter type is either int or long. The problem with this approach, as seen in the example below, is that applications of this concept can't infer template parameters. For example, the usages of call() below require that template parameters be explicitly listed out.
// https://godbolt.org/z/E519s8Pso
//
#include <concepts>
#include <iostream>
// Concept for a callable that can take a single parameter or either int or long.
template<typename T, typename P>
concept MySpecialFunction =
(std::same_as<P, int> || std::same_as<P, long>)
&& requires(T t, P l) {
{ t(l) } -> std::same_as<decltype(l)>;
};
// T must be callable with 1 parameter that is either int or long!
template<typename T, typename P>
requires MySpecialFunction<T, P>
decltype(auto) call(T t) {
return t(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
int main() {
std::cout << call<decltype(square_int), int>(square_int) << std::endl;
std::cout << call<decltype(square_long), long>(square_long) << std::endl;
return 0;
}
My second attempt was to explode out the concept to one for int and one for long, then combine them together in a third concept. In this version, the usages of call() below don't require that template parameters be explicitly listed out, but the concept is more verbose. Imagine how something like this would look if there were more than 20 types instead of just 2.
// https://godbolt.org/z/hchT11rMx
//
#include <concepts>
#include <iostream>
// Concept for a callable that can take a single parameter or either int or long.
template<typename T>
concept MySpecialFunction1 = requires(T t, int i) {
{ t(i) } -> std::same_as<decltype(i)>;
};
template<typename T>
concept MySpecialFunction2 = requires(T t, long l) {
{ t(l) } -> std::same_as<decltype(l)>;
};
template<typename T>
concept MySpecialFunction = MySpecialFunction1<T> || MySpecialFunction2<T>;
// T must be callable with 1 parameter that is either int or long!
template<MySpecialFunction T>
decltype(auto) call(T t) {
return t(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
int main() {
std::cout << call(square_int) << std::endl;
std::cout << call(square_long) << std::endl;
return 0;
}
Is there anyway to have the conciseness / easy of understanding that the first example gives without the compiler losing the ability to infer template parameters as happens in the second example?
Since you have the types int and long baked into the concept, why don't you use something like this:
//true if Fn accepts one parameter (int or long) and have the same type as the result
template <typename Fn>
concept MySpecialFunction = requires (Fn fn) {
requires
std::same_as<int, decltype(fn(0))> ||
std::same_as<long, decltype(fn(0L))>;
};
template <MySpecialFunction Fn>
decltype(auto) call(Fn fn)
{
//think about what happens if Fn is square_long
//doesn't the type of the value (int) gets converted to long?
//that raises the question what your main purpose is
return fn(2);
}
See: https://godbolt.org/z/Y7vTrPoP5
Maybe you also want to have a look at:
https://en.cppreference.com/w/cpp/concepts/invocable
https://en.cppreference.com/w/cpp/utility/functional/invoke
After some browsing around, I came across https://stackoverflow.com/a/43526780/1196226 and https://stackoverflow.com/a/22632571/1196226. I was able to utilize these answers to build out a solution that can apply concepts to parameters concisely and without the compiler losing the ability to infer template parameters.
// https://godbolt.org/z/nh8nWxhzK
//
#include <concepts>
#include <iostream>
template <std::size_t N, typename T0, typename ... Ts>
struct typeN { using type = typename typeN<N-1U, Ts...>::type; };
template <typename T0, typename ... Ts>
struct typeN<0U, T0, Ts...> { using type = T0; };
template <std::size_t, typename F>
struct argN;
template <std::size_t N, typename R, typename ... As>
struct argN<N, R(*)(As...)> { using type = typename typeN<N, As...>::type; }; // needed for std::integral<>
template <std::size_t N, typename R, typename ... As>
struct argN<N, R(As...)> { using type = typename typeN<N, As...>::type; }; // needed for std::is_integeral_v<>
template <typename F>
struct returnType;
template <typename R, typename ... As>
struct returnType<R(*)(As...)> { using type = R; }; // works for std::integral<> / std::same_as<>
template <typename R, typename ... As>
struct returnType<R(As...)> { using type = R; }; // needed for std::is_integeral_v<>
template<typename Fn>
concept MySpecialFunction =
(std::same_as<typename argN<0U, Fn>::type, int> || std::same_as<typename argN<0U, Fn>::type, long>)
&& std::same_as<typename returnType<Fn>::type, typename argN<0U, Fn>::type>;
template<MySpecialFunction Fn>
decltype(auto) call(Fn fn) {
return fn(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
static_assert( std::is_integral_v<typename argN<0U, decltype(square_int)>::type> );
static_assert( std::is_integral_v<typename returnType<decltype(square_int)>::type> );
static_assert( std::is_integral_v<typename argN<0U, decltype(square_long)>::type> );
static_assert( std::is_integral_v<typename returnType<decltype(square_long)>::type> );
int main() {
std::cout << call(square_int) << std::endl;
std::cout << call(square_long) << std::endl;
return 0;
}
Lets say I have a tuple
std::tuple<Operation<1>, Operation<2>, Operation<3>>. Operation<> has a member function with the signature SomeType someFunction(SomeType). What I want to do is to call the operations successively such that the resulting order of calls would be Operation<3>::someFunction(Operation<2>::someFunction(Operation<1>::someFunction())) and I would get the final SomeType value. How do I achieve this using variadic templates (I have access to C++17)?
I can call each member function with std::apply([](auto& ...x) { (..., x.someFunction()); }, tuple); but what kind of expression do I need to call someFunction() with the output of the previous call?
I suppose you can combine std::apply() and template folding with a lambda as follows
auto l = [&val](auto ... Ops)
{ ((val = Ops.someFunc(val)), ...); };
The following is a full working example
#include <tuple>
#include <iostream>
template <int I>
struct Oper
{
static constexpr int someFunc (int i)
{ return i + I; }
};
int main ()
{
std::tuple<Oper<1>, Oper<2>, Oper<3>, Oper<4>> t;
int val {}; // starting value
auto l = [&val](auto ... Ops)
{ ((val = Ops.someFunc(val)), ...); };
std::apply(l, t);
std::cout << val << std::endl;
}
#max66's solution is elegant and concise, however one caveat is that all your operations must handle and return the same type (which is your case), I will try to propose a broader approach.
The idea is to rely on an overloaded operator>> to apply the desired operation on a state and the next step. To do so let's first define some building blocks:
// Just to avoid the hassle of std::forwarding by hand everywhere
#define CPPFWD(x) std::forward<decltype(x)>(x)
// We do not want to pollute the global namespace with our special operator>>
namespace combine {
// This will make the appropriate functor for each step
template <typename T, typename Op>
auto make_operation(T&& tuple_element, Op&& op) {
return [ el = CPPFWD(tuple_element),
op = CPPFWD(op) ](auto&& input) mutable {
return op(el, CPPFWD(input));
};
}
template <typename Input, typename Op>
auto operator>>(Input&& input, Op&& op) {
return CPPFWD(op)(CPPFWD(input));
}
} // ns combine
Now we are ready to tackle the left fold implementation:
template <typename State, typename Tuple, typename Op, size_t... Is>
auto fold_left_impl(State&& state, Tuple&& tuple, Op&& op, std::index_sequence<Is...>) {
using combine::make_operation;
// We want our operator>> to be in the immediate scope here
// to avoid selecting an inappropriate hypothetical overload
using combine::operator>>;
using std::get;
return (CPPFWD(state) >> ... >> make_operation(get<Is>(CPPFWD(tuple)), op));
}
Finally the function exposed to the end-user:
template <typename T>
using remove_cvref_t = std::remove_cv_t< std::remove_reference_t< T > >;
template <typename State, typename Tuple, typename Op>
auto fold_left(State&& state, Tuple&& tuple, Op&& op) {
return fold_left_impl(
CPPFWD(state),
CPPFWD(tuple),
CPPFWD(op),
std::make_index_sequence< std::tuple_size< remove_cvref_t< Tuple > >::value > {} );
}
In your case, the correct usage would be:
std::tuple<Operation<1>, Operation<2>, Operation<3>> t;
fold_left(
0,
t,
[](auto&& op, auto&& in) {
return CPPFWD(op).someFunc(CPPFWD(in));
} );
A live example can be found on Coliru
I'm struggling with some template programming and I hope you can give me some help. I coded a C++11 interface that, given some structs like:
struct Inner{
double a;
};
struct Outer{
double x, y, z, r;
Inner in;
};
Implements a getter/setter to the real data that is customized to the specified struct members:
MyData<Outer, double, &Outer::x,
&Outer::y,
&Outer::z,
&Outer::in::a //This one is not working
> state();
Outer foo = state.get();
//...
state.set(foo);
I managed to implement this for simple structs in the following way:
template <typename T, typename U, U T::* ... Ms>
class MyData{
std::vector<U *> var;
public:
explicit MyData();
void set(T const& var_);
T get() const;
};
template <typename T, typename U, U T::* ... Ms>
MyData<T, U, Ms ... >::Struct():var(sizeof...(Ms))
{
}
template <typename T, typename U, U T::* ... Ms>
void MyData<T, U, Ms ...>::set(T const& var_){
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
*var[i++] = var_.*d;
}
}
template <typename T, typename U, U T::* ... Ms>
T MyData<T, U, Ms ...>::get() const{
T var_;
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
var_.*d = *var[i++];
}
return var_;
}
But it fails when I pass a member of a nested struct. Ideally, I'd like to implement a generic pointer to member type that allows me to be compatible with several levels of scope resolutions. I found this approach, but I'm not sure if this should be applied to my problem or if there exists some implementation ready to use. Thanks in advance!
Related posts:
Implicit template parameters
Pointer to inner struct
You might wrap member pointer into struct to allow easier chaining:
template <typename...> struct Accessor;
template <typename T, typename C, T (C::*m)>
struct Accessor<std::integral_constant<T (C::*), m>>
{
const T& get(const C& c) { return c.*m; }
T& get(C& c) { return c.*m; }
};
template <typename T, typename C, T (C::*m), typename ...Ts>
struct Accessor<std::integral_constant<T (C::*), m>, Ts...>
{
auto get(const C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
auto get(C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
};
template <typename T, typename U, typename ...Ts>
class MyData
{
std::vector<U> vars{sizeof...(Ts)};
template <std::size_t ... Is>
T get(std::index_sequence<Is...>) const
{
T res;
((Ts{}.get(res) = vars[Is]), ...); // Fold expression C++17
return res;
}
template <std::size_t ... Is>
void set(std::index_sequence<Is...>, T const& t)
{
((vars[Is] = Ts{}.get(t)), ...); // Fold expression C++17
}
public:
MyData() = default;
T get() const { return get(std::index_sequence_for<Ts...>()); }
void set(const T& t) { return set(std::index_sequence_for<Ts...>(), t); }
};
With usage similar to
template <auto ...ms> // C++17 too
using Member = Accessor<std::integral_constant<decltype(ms), ms>...>;
MyData<Outer, double, Member<&Outer::x>,
Member<&Outer::y>,
Member<&Outer::z>,
Member<&Outer::in, &Inner::a>
> state;
std::index_sequence is C++14 but can be implemented in C++11.
Folding expression from C++17 can be simulated too in C++11.
typename <auto> (C++17) should be replaced by typename <typename T, T value>.
Demo
A generalization of a member pointer is a function that can map T to X& at compile time.
In c++17 it isn't hard to wire things up thanks to auto. In c++11 it gets harder. But the basic idea is that you don't actually pass member pointers, you pass types, and those types know how to take your class and get a reference out of them.
template<class T, class D, class...Fs>
struct MyData {
std::array<D*, sizeof...(Fs)> var = {};
explicit MyData()=default;
void set(T const& var_) {
var = {{ Fs{}(std::addressof(var_))... }};
}
T get() {
T var_;
std::size_t index = 0;
using discard=int[];
(void)discard{ 0, (void(
*Fs{}(std::addressof(var_)) = *var[index++]
),0)... };
return var_;
}
};
it remains to write a utility that makes writing the Fs... easy for the member pointer case
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M > {
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
#define TYPE_N_VAL(...) \
decltype(__VA_ARGS__), __VA_ARGS__
#define MEM_PTR(...) get_ptr_to_member_t< TYPE_N_VAL(__VA_ARGS__) >
now the basic case is
MyData< Outer, double, MEM_PTR(&Outer::x), MEM_PTR(&Outer::y) >
The more complex case can now be handled.
An approach would be to teach get_ptr_to_member to compose. This is annoying work, but nothing fundamental. Arrange is so that decltype(ptr_to_member_t * ptr_to_member_t) returns a type that instances right, applies it, then takes that pointer and runs the left hand side on it.
template<class First, class Second>
struct composed;
template<class D>
struct composes {};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class First, class Second>
struct composed:composes<composed<First, Second>> {
template<class In>
auto operator()(In&& in) const
RETURNS( Second{}( First{}( std::forward<In>(in) ) ) )
};
template<class First, class Second>
composed<First, Second> operator*( composes<Second> const&, composes<First> const& ) {
return {};
}
then we upgrade:
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M >:
composes<get_ptr_to_member_t< D T::*, M >>
{
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
and now * composes them.
MyData<TestStruct, double, MEM_PTR(&Outer::x),
MEM_PTR(&Outer::y),
MEM_PTR(&Outer::z),
decltype(MEM_PTR(&Inner::a){} * MEM_PTR(&Outer::in){})
> state();
answre probably contains many typos, but design is sound.
In c++17 most of the garbage evaporates, like the macros.
I would use lambda approach to implement similar functionalities in C++17(C++14 is also ok, just change the fold expression):
auto access_by() {
return [] (auto &&t) -> decltype(auto) {
return decltype(t)(t);
};
}
template<class Ptr0, class... Ptrs>
auto access_by(Ptr0 ptr0, Ptrs... ptrs) {
return [=] (auto &&t) -> decltype(auto) {
return access_by(ptrs...)(decltype(t)(t).*ptr0);
};
}
auto data_assigner_from = [] (auto... accessors) {
return [=] (auto... data) {
return [accessors..., data...] (auto &&t) {
((accessors(decltype(t)(t)) = data), ...);
};
};
};
Let's see how to use these lambdas:
struct A {
int x, y;
};
struct B {
A a;
int z;
};
access_by function can be used like:
auto bax_accessor = access_by(&B::a, &A::x);
auto bz_accessor = access_by(&B::z);
Then for B b;, bax_accessor(b) is b.a.x; bz_accessor(b) is b.z. Value category is also preserved, so you can assign: bax_accessor(b) = 4.
data_assigner_from() will construct an assigner to assign a B instance with given accessors:
auto data_assigner = data_assigner_from(
access_by(&B::a, &A::x),
access_by(&B::z)
);
data_assigner(12, 3)(b);
assert(b.z == 3 && b.a.x == 12);
I am writing a method to extract values from arbitrarily nested structs. I am almost there, but would like to also provide an option to convert the value retrieved (by default no conversion). Since parameter packs can't be followed by another template parameter, I have to fudge this a bit. The below works except for the indicated line:
#include <iostream>
#include <type_traits>
typedef struct {
int a;
int b;
} bar;
typedef struct {
int c;
bar d;
} baz;
template <typename T, typename S, typename... Ss>
auto inline getField(const T& obj, S field1, Ss... fields)
{
if constexpr (!sizeof...(fields))
return obj.*field1;
else
return getField(obj.*field1, fields...);
}
template <typename Obj, typename Out, class ...C, typename... T>
auto inline getFieldC(const Obj& obj, Out, T C::*... field)
{
return static_cast<Out>(getField(obj, field...));
}
template<class T> struct tag_t { using type = T; };
template<class...Ts>
using last = typename std::tuple_element_t< sizeof...(Ts) - 1, std::tuple<tag_t<Ts>...> >::type;
template <typename Obj, typename... T>
auto getMyFieldWrapper(const Obj& obj, T... field)
{
if constexpr (std::is_member_object_pointer_v<last<Obj, T...>>)
return getField(obj, field...);
else
return getFieldC(obj, last<Obj, T...>{}, field...); // <- this doesn't compile, need a way to pass all but last element of field
}
int main()
{
baz myObj;
std::cout << getMyFieldWrapper(myObj, &baz::c); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b, 0.); // doesn't work
}
How do I implement the indicated line? I'm using the latest MSVC, and am happy to make full use of C++17 to keep things short and simple.
Usually more helpful to invert the flow. First, write a higher-order function that forwards an index sequence:
template <typename F, size_t... Is>
auto indices_impl(F f, std::index_sequence<Is...>) {
return f(std::integral_constant<size_t, Is>()...);
}
template <size_t N, typename F>
auto indices(F f) {
return indices_impl(f, std::make_index_sequence<N>());
}
That is just generally useful in lots of places.
In this case, we use it to write a higher-order function to drop the last element in a pack:
template <typename F, typename... Ts>
auto drop_last(F f, Ts... ts) {
return indices<sizeof...(Ts)-1>([&](auto... Is){
auto tuple = std::make_tuple(ts...);
return f(std::get<Is>(tuple)...);
});
}
And then you can use that:
return drop_last([&](auto... elems){
return getMyField(obj, last<Obj, T...>{}, elems...);
}, field...);
References omitted for brevity.
Of course, if you want to combine both and just rotate, you can do:
// Given f and some args t0, t1, ..., tn, calls f(tn, t0, t1, ..., tn-1)
template <typename F, typename... Ts>
auto rotate_right(F f, Ts... ts) {
auto tuple = std::make_tuple(ts...);
return indices<sizeof...(Ts)-1>([&](auto... Is){
return f(
std::get<sizeof...(Ts)-1>(tuple),
std::get<Is>(tuple)...);
});
}
used as:
return rotate_right([&](auto... elems){
return getMyField(obj, elems...);
}, field...);
How do I implement the indicated line?
Not sure to understand what do you want but... it seems to me that you can make it calling an intermediate function
template <std::size_t ... Is, typename ... Ts>
auto noLastArg (std::index_sequence<Is...> const &,
std::tuple<Ts...> const & tpl)
{ return getMyField(std::get<Is>(tpl)...); }
you can rewrite your function as follows
template <typename Obj, typename ... T>
auto getMyFieldWrapper (Obj const & obj, T ... field)
{
if constexpr (std::is_member_object_pointer<last<Obj, T...>>::value )
return getMyField(obj, field...);
else
return noLastArg(std::make_index_sequence<sizeof...(T)>{},
std::make_tuple(obj, field...));
}
The idea is pack the arguments for getMyField in a std::tuple of sizeof...(T)+1u elements (+1 because there is also obj) and call getMyField() unpacking the first sizeof...(T) of them.
But isn't clear, to me, if you want also last<Obj, T...>{}.
In this case, the call to noLastArg() become
return noLastArg(std::make_index_sequence<sizeof...(T)+1u>{},
std::make_tuple(obj, last<Obj, T...>{}, field...));
I have a family of classes with methods with the following signature:
double compute(list<T> pars)
This method performs a calculation with the parameters received through pars. For each compute(list) method, I have another compute(x1, x2, ..., xn) which is the method implementing the real calculation. Thus, compute(pars) should do some such as:
double compute(list<T> pars)
{
T x1 = list.pop_back();
T x2 = list.pop_back();
// .. so on until last parameter xn
T xn = list.pop_back();
return compute(x1, x2, .., xn); // here the real implementation is called
}
This pattern repeats many times, the only thing that could change is the size of pars list and of course the implementation of compute(x1, x1, ..).
I would like to find a way for "driying" this repetitive process; concretely, extracting the parameters in pars list and building the call to compute(x1, x2, .., xn). I have been trying without success to do some macro tricks.
My question is if it exists some way based on metaprogramming that allows me to implement compute(list<T> pars) once and simply reuse it n order to perform the call to compute(x1, x2, ..., xn)
EDIT: This is the signature of the other compute(x1, ...)
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
'VtlQuantityis a class representingdouble`'s, their units and other stuff.
You may do the following:
template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
std::vector<T> v(pars.rbegin(), pars.rend());
return std::forward<Func>(f)(v.at(Is)...);
}
template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
With usage similar to:
apply<6>(print, l);
Demo
To compute automatically the arity of the function, you may create a traits:
template <typename F> struct arity;
template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
static constexpr std::size_t value = sizeof...(Args);
};
and then
template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
Demo
You have to enrich arity to support Functor (as the lambda).
This is a C++11 solution for the more general problem type of applying
a function or functor F, taking N type T parameters and returning type Ret, to the N arguments
at successive positions of some input iterator.
This gains several flexibilities over a solution parameterized by some container-of-T of the arguments:-
You can extract the arguments from an arbitrary N-sized range within a sequence.
The sequence need not be a container-of-T - though it must be a sequence of something convertible to T.
You can extract the arguments either last-to-first (as you do), or first-to-last,
from the standard container types or any that support forward and reverse iterators.
You may even apply F to arguments consumed directly from some input stream, without
intermediate extraction.
And of course you can change your mind about the type of sequence in which
to deliver arguments without having to change the functional-application solution.
Interface
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop());
You can use this like:
auto result = invoke(func,iter);
to apply func to the arguments at N successive positions of the iterator
iter.
That way, you get no range-checking that N arguments are legitimately accessible
to your program at those positions. The range-checking code that you will spot
in the implementation will compile to nothing and if you trespass out of bounds
there will be UB.
If you want range checking you can instead code:
auto result = invoke(func,iter,end);
where end is an iterator of the same type as iter delimiting the end of the
available range in the usual manner. In this case an std::out_of_range will
be thrown if N exceeds the size of the range.
Implementation
#include <type_traits>
#include <functional>
#include <string>
template<typename T>
struct function_traits;
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<Ret(*)(ArgT, ArgRest...)>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<std::function<Ret(ArgT, ArgRest...)>>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
namespace detail {
template<typename Left, typename Right>
typename std::enable_if<!std::is_same<Left,Right>::value>::type
range_check(Left, Right, std::string const &){}
template<typename Left, typename Right>
typename std::enable_if<std::is_same<Left,Right>::value>::type
range_check(Left start, Right end, std::string const & gripe) {
if (start == end) {
throw std::out_of_range(gripe);
}
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N == function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter, Stop, Ts...args)
{
return f(args...);
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N != function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter it, Stop stop, Ts...args)
{
range_check(it,stop,
"Function takes more arguments than are available "
"in `" + std::string(__PRETTY_FUNCTION__) + '`');
using arg_type = typename
function_traits<typename std::decay<Func>::type>::first_arg_type;
auto arg = static_cast<arg_type>(*it);
return invoke<N + 1>(std::forward<Func>(f),++it,stop,args...,arg);
}
} // namespace detail
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop())
{
return detail::invoke<0>(std::forward<Func>(f),it,stop);
}
The two specializations of function_traits<T> provided will restrict
compilation to functional types T that take at least one argument, which should
suffice for likely applications. Should you need to support
invocation on types taking 0 arguments then you can augment them with:
template <typename Ret>
struct function_traits<Ret(*)()>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
template <typename Ret>
struct function_traits<std::function<Ret()>>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
The specialization for free functions function_traits<Ret(*)(ArgT, ArgRest...)>,
is strictly a redundant convenience, since they too could be wrapped in std::function
objects, as you're obliged to do for anything fancier than a free function.
Demo
For a program that exercises the features discussed you can append:
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <sstream>
#include <iterator>
struct num
{
double d;
explicit operator double() const {
return d;
}
};
double add4(double d0, double d1, double d2, double d3)
{
std::cout << d0 << '+' << d1 << '+' << d2 << '+' << d3 << "\n=";
return d0 + d1 + d2 + d3;
}
int multiply2(int i0, int i1)
{
std::cout << i0 << '*' << i1 << "\n=";
return i0 * i1;
}
struct S
{
int subtract3(int i0, int i1, int i2) const
{
std::cout << i0 << '-' << i1 << '-' << i2 << "\n=";
return i0 - i1 - i2;
}
int compute(std::list<int> const & li) const {
std::function<int(int,int,int)> bind = [this](int i0, int i1, int i2) {
return this->subtract3(i0,i1,i2);
};
return invoke(bind,li.begin());
}
};
int main()
{
std::vector<double> vd{1.0,2.0,3.0,4.0};
std::vector<double> vdshort{9.0};
std::list<int> li{5,6,7,8};
std::deque<num> dn{num{10.0},num{20.0},num{30.0},num{40.0}};
std::istringstream iss{std::string{"10 9 8"}};
std::istream_iterator<int> it(iss);
std::cout << invoke(add4,vd.rbegin()) << '\n';
std::cout << invoke(multiply2,li.begin()) << '\n';
std::cout << invoke(add4,dn.rbegin()) << '\n';
std::cout << invoke(multiply2,++it) << '\n';
S s;
std::cout << '=' << s.compute(li) << '\n';
try {
std::cout << invoke(add4,vdshort.begin(),vdshort.end()) << '\n';
} catch(std::out_of_range const & gripe) {
std::cout << "Oops :(\n" << gripe.what() << '\n';
}
return 0;
}
The case of:
S s;
std::cout << '=' << s.compute(li) << '\n';
is particularly pertinent to your particular problem, since here we call
S::compute(std::list<int> const & li) to apply another non-static method
of S to arguments delivered in the list li. See in the implementation
of S::compute how the use of a lambda can conveniently bind both the
calling S object and S::compute into an std::function we can
pass to invoke.
Live demo
C++17 solution below. wandbox link
(Greatly simplified thanks to Jarod42)
Assumes the number of arguments N is known at compile-time, but the list can have any size.
This calls pop_back() multiple times as shown in the example, then calls a function.
template <typename T>
struct list
{
T pop_back() { return T{}; }
};
namespace impl
{
template<typename TList, std::size_t... TIs>
auto list_to_tuple(TList& l, std::index_sequence<TIs...>)
{
using my_tuple = decltype(std::make_tuple((TIs, l.pop_back())...));
return my_tuple{((void)TIs, l.pop_back())...};
}
}
template<std::size_t TN, typename TList>
auto list_to_tuple(TList& l)
{
return impl::list_to_tuple(l, std::make_index_sequence<TN>());
}
template <std::size_t TN, typename TList, typename TF>
auto call_with_list(TList& l, TF&& f)
{
return std::experimental::apply(f, list_to_tuple<TN>(l));
}
void test_compute(int, int, int)
{
// ...
}
int main()
{
list<int> l{};
call_with_list<3>(l, test_compute);
}
How does it work?
The idea is that we "convert" a list to a tuple, specifying how many elements we want to pop from the list at compile-time using list_to_tuple<N>(list).
After getting a tuple from the list, we can use std::experimental::apply to call a function by applying the elements of the tuple as arguments: this is done by call_with_list<N>(list, func).
To create a tuple from the list, two things needs to be done:
Creating an std::tuple<T, T, T, T, ...>, where T is repeated N times.
Call list<T>::pop_back() N times, putting the items in the tuple.
To solve the first problem, decltype is used to get the type of the following variadic expansion: std::make_tuple((TIs, l.pop_back())...). The comma operator is used so that TIs, l.pop_back() evaluates to decltype(l.pop_back()).
To solve the second problem, a variadic expansion is used inside the std::initializer_list tuple constructor, which guarantees order-of-evaluation: return my_tuple{((void)TIs, l.pop_back())...};. The same comma operator "trick" described above is used here.
Can I write it in C++11?
Yes, but it will be slightly more "annoying".
std::experimental::apply is not available: look online for solutions like this one.
std::index_sequence is not available: you will have to implement your own.
template<class T> using void_t = void;
template<class T, class F, std::size_t N=0, class=void>
struct arity:arity<T, F, N+1> {};
template<class F, class T, class Indexes>
struct nary_result_of{};
template<std::size_t, class T>
using ith_T=T;
template<class F, class T, std::size_t...Is>
struct nary_result_of<F, T, std::index_sequence<Is...>>:
std::result_of<F( ith_T<Is, T> )>
{};
template<class T, class F, std::size_t N>
struct arity<T, F, N, void_t<
typename nary_result_of<F, T, std::make_index_sequence<N>>::type
>>:
std::integral_constant<std::size_t, N>
{};
arity uses one C++14 feature (index sequences, easy to write in C++11).
It takes types F and a T and tells you the least number of Ts you can pass to F to make the call valid. If no number of T qualify, it blows your template instantiation stack and your compiler complains or dies.
template<class T>
using strip = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
namespace details {
template<class T, std::size_t N, class F, class R,
std::size_t...Is
>
auto compute( std::index_sequence<Is...>, F&& f, R&& r ) {
std::array<T, N> buff={{
(void(Is), r.pop_back())...
}};
return std::forward<F>(f)( buff[Is]... );
}
}
template<class F, class R,
class T=strip< decltype( *std::declval<R&>().begin() ) >
>
auto compute( F&& f, R&& r ) {
return details::compute( std::make_index_sequence<arity<F,T>{}>{}, std::forward<F>(f), std::forward<R>(r) );
}
The only thing really annoying to convert to C++11 is the auto return type on compute. I'd have to rewrite my arity.
This version should auto detect the arity of even non-function pointers, letting you call this with lambdas or std::functions or what have you.