Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ : How to pass function pointer, stored in a local variable, as a template parameter

using namespace std;

float test1(float i){
    return i * i;
}

int test2(int i){
    return i+9;
}

struct Wrapper{

    typedef void (*wrapper_type)(int);

    template<class R, class A>
    void wrap(string name,R (*fn) (A) ){
        wrapper_type f_ = &Wrapper::wrapper1<R,A,fn>;
        // in the real program, f_ would be passed in an array to some c library
        wrappers[name] = f_;
    }

    void run(int i){
        map<string,wrapper_type>::iterator it, end = wrappers.end();
        for(it=wrappers.begin();it!=end;it++){
            wrapper_type wt = (*it).second;
            (*wt)(i);
        }
    }

    template<class R, class A, R (*fn) (A)>
    static void wrapper1(int mul){
        //do something
        A arg = mul;
        R result = (*fn)( arg );
        cout << "Result: " << result << endl;
    }

    map<string,wrapper_type> wrappers;

};

int main(int argc, char** argv) {
    Wrapper w;
    w.wrap("test1",&test1);
    w.wrap("test2",&test2);
    w.run(89);
    return 0;
}

Here's the g++ error:

main.cpp:31: error: ‘fn’ is not a valid template argument for type ‘float (*)(float)’ because function ‘fn’ has not external linkage

From what i understand, the problem is that a local variable has no linkage; thus it can not be used as a template parameter.

So, i wanted to know if there's a way to get around this problem or another technique to accomplish the same ?

Thanks.

Edit 1:

I totally understand that I can't pass a value that can not be determined at compile time as a template paramter. What i'm asking is - is there a better way to do this? Right now the solution that works for me is :

template<class R, class A,R (*fn) (A)>
void wrap(string name){
    wrapper_type f_ = &Wrapper::wrapper1<R,A,fn>;
    // in the real program, f_ would be passed in an array to sm c library
    wrappers[name] = f_;
}

and called as :

w.wrap<float, float, &test1>("test1");
w.wrap<int, int, &test2>("test2");

But here I've to pass argument-type during wrapping. Is there someway to avoid this ?

EDIT 2:

Just to clarify or add more info: The interface I want to present to the user has to be similar to LuaBind or Boost.Python i.e. Wrapper.wrap("test1",&test1); should be sufficient.

like image 417
Code freak Avatar asked Jan 20 '23 17:01

Code freak


2 Answers

Template parameters must be known at compile time, so you'll have to redesign your code taking this into account.

Edit: for your updated question use Boost function_traits.

template<R (*fn) (A)>
void wrap(string name){
    wrapper_type f_ = &Wrapper::wrapper1<function_traits<A>::result_type, function_traits<A>::arg1_type,fn>;
    // in the real program, f_ would be passed in an array to sm c library
    wrappers[name] = f_;
}
like image 197
AProgrammer Avatar answered Jan 30 '23 08:01

AProgrammer


what about encapsulating your "test*" functions in classes sharing a common interface and then passing the classes into the templates?

like image 39
sergio Avatar answered Jan 30 '23 06:01

sergio