0

For mathematical purposes, I want to write a generic functor class to wrap simple mathematical equations with both variables and parameters. Imagine a simple parabola:

y=f(x;a,b,c)=a*x*x+b*x+c  

Here a, b, and c are parameters set one time for one parabola. Then the ordinate value y is computed by providing certain values of the variable x. I've written a simple gen_func class using variadic templates in combination with std::tuple for holding parameters and a variadic parameter pack for variables. The skeleton for this class was found at How to store variadic template arguments?

I am new to the new c++11 standard and I know that writing correct code using variadic templates is a very complex thing.

For some purposes, I want to change the variable by call-by-reference. But here, the compiler (gcc 4.7) does complain. For simplicity, i tested the code with a simple function change. It's only purpose is to alter the value of the passed first variable. Here is the code of a simple program:

#include <tuple>
#include <iostream>
#include <functional>
#include<utility>
using namespace std;

// Compile with : g++ -o X.bin -Wall -pedantic -std=c++11 X.cpp 

// Stuff for compile-time recursive parameter pack expansion:
namespace generic
{
    template <std::size_t... Ts>
    struct index_sequence {}; 

    template <std::size_t N, std::size_t... Ts>
    struct sequence_generator : sequence_generator<N - 1, N - 1, Ts...> {};

    template <std::size_t... Ts>
    struct sequence_generator<0, Ts...> : index_sequence<Ts...> {};

    template<  typename ReturnType , typename... Args  > using generic_function_pointer = ReturnType (*) (  Args... args  ) ;

}


// The generic Function wrapper: 
template < typename R , class F , typename... Ps>
class gen_func
{
    public:
        // Constructor
        gen_func(F&& f,Ps&&...parms){
            func = forward<F>(f); 
            parameters=make_tuple(forward<Ps>(parms)...);
        }
        // execute-Function for public call:
        template<typename... Vars>
        R execute(Vars&&... vars) { 
           return f_decon<Vars... , Ps...>( forward<Vars>(vars)... , parameters );
        }
        //
    private:
         // "Deconvolution Function"
        template<typename... Vars , typename... Ts >
        R f_decon( Vars&&... vars , tuple<Ts...>& tup ){
            return f_proxy<Vars...,Ps...>( forward<Vars>(vars)... , tup , generic::sequence_generator<sizeof...(Ts)>{} );
        }
        // "Proxy-Function" calling the wrapped Function func:
        template<typename... Vars , typename... Ts , size_t... Is>
        R f_proxy( Vars&&... vars , tuple<Ts...>& tup , generic::index_sequence<Is...> ){
            return func( forward<Vars>(vars)... , get<Is>(tup)... );
        }
        // The wrapped Function f
        F func;
        // The tuple holding optional parameters for function f:
        tuple<Ps...> parameters; 

};

// Partial template Specialization of gen_func for Functions with Return Type void:
template <class F , typename... Ps>
class gen_func<void,F,Ps...>
{
    public:
        // Constructor
        gen_func(F&& f,Ps&&...parms){
            func = forward<F>(f); 
            parameters=make_tuple(forward<Ps>(parms)...);
        }
        // execute-Function for public call:
        template<typename... Vars>
        void execute(Vars&&... vars) { 
           f_decon<Vars... , Ps...>( forward<Vars>(vars)... , parameters );     // Line 75
        }
        //
    private:
        // The wrapped Function f
        F func;
        // The tuple holding optional parameters for function f:
        tuple<Ps...> parameters;
         // "Deconvolution Function"
        template<typename... Vars , typename... Ts >                            
        void f_decon( Vars&&... vars , tuple<Ts...>& tup ){                     // Line 85
            f_proxy<Vars...,Ts...>( forward<Vars>(vars)... , tup , generic::sequence_generator<sizeof...(Ts)>{} );
        }
        // "Proxy-Function" calling the wrapped Function func:
        template<typename... Vars , typename... Ts , size_t... Is>
        void f_proxy( Vars&&... vars , tuple<Ts...>& tup , generic::index_sequence<Is...> ){
            func( forward<Vars>(vars)... , get<Is>(tup)... );
        }
};

void change(int& m,int n){
    cout << "Changing!" << endl;
    int res=42+n;
    m=res;
}  

int main()
{

typedef generic::generic_function_pointer<void,int&,int>  chg_ptr;

gen_func<void,chg_ptr> y(change);

// The Variable to be altered by change    
int j=0;
cout << "j='" << j << "'" << endl; // should be 0
// Wrapped Function call:
y.execute(j,21);                                        // Line 113
cout << "j='" << j << "'" << endl; // should be 63

return 0;
}  

The compiler does complain with:

X.cpp: In instantiation of ‘void gen_func<void, F, Ps ...>::execute(Vars&& ...) [with Vars = {int&, int}; F = void (*)(int&, int); Ps = {}]’:
X.cpp:113:15:   required from here
X.cpp:75:12: error: no matching function for call to ‘gen_func<void, void (*)(int&, int)>::f_decon(int&, int, std::tuple<>&)’
X.cpp:75:12: note: candidate is:
X.cpp:85:14: note: template<class ... Vars, class ... Ts> void gen_func<void, F, Ps ...>::f_decon(Vars&& ..., std::tuple<_Args2 ...>&) [with Vars = {Vars ...}; Ts = {Ts ...}; F = void (*)(int&, int); Ps = {}]
X.cpp:85:14: note:   template argument deduction/substitution failed:
X.cpp:75:12: note:   mismatched types ‘std::tuple<_Elements ...>’ and ‘int’

It seems, that the code is wrong by passing the variable x by reference so that it could be altered by wrapped function change. Is my program approach completely wrong? Is it simple to correct? Are there better ways to achieve my goal? Thanks in advance for any advice!

Community
  • 1
  • 1
  • You might want to learn about the [*member initializer list*](http://stackoverflow.com/q/926752/420683). – dyp Jul 02 '14 at 15:32
  • I don't quite see why you need that specialization. – dyp Jul 02 '14 at 15:34
  • Might be a g++ bug? It compiles with clang++3.5 – dyp Jul 02 '14 at 15:37
  • Uhhhh this is quite ugly. You can work around that issue by deducing all template parameters. This can be achieved by changing the order of parameters for the member functions `f_decon` and `f_proxy`: [Live example](http://coliru.stacked-crooked.com/a/51d7c08bb057ef3e) – dyp Jul 02 '14 at 15:41
  • [Here's a near-minimal example](http://coliru.stacked-crooked.com/a/44563e7137b884e7) reproducing the problem for g++. Works fine with clang++ – dyp Jul 02 '14 at 15:59
  • Works with g++4.10 I suspect it's a g++ bug and will try to find a bug report. – dyp Jul 02 '14 at 16:01

0 Answers0