1

I'm using ROOT Cern to solve a multi-variable non-linear system of equations. For some problems I have 4 functions and 4 variables. However, for others I need 20 functions with 20 variables. I'm using a class called "WrappedParamFunction" to wrap the functions and then I add the wrapped functions to the "GSLMultiRootFinder" to solve them. The function is wrapped this way:

ROOT::Math::WrappedParamFunction<> g0(&f0, "number of variables", "number of parameters");

Therefore, I need to declare the f0...fi functions before my void main(){} part of the code. I'm declaring the functions in this way:

double f0(const double *x, const double *par){return -par[0]+y[0]*par[1];}
double f1(const double *x, const double *par){return -par[1]+y[1]*par[2];}
.
.

Is there a way to create those functions inside a loop and stack them in an array? Something like this:

double (*f[20])(const double *x, const double *par);
for(int i=0;i<20;i++){
   f[i]= -par[i]+x[i]*par[i+1];
}

So that later I can wrap the functions in this way:

ROOT::Math::WrappedParamFunction<> g0(f[0], "number of variables", "number of parameters");
6
  • Are you familiar with lambda functions? WrappedParamFunction looks like it can hold a lambda Commented Aug 26, 2022 at 15:30
  • You can create an array of function pointers before the loop. The main caveat is that the functions must have the same signature. Commented Aug 26, 2022 at 15:32
  • Another option is to use a switch statement or std::map with function pointers. You could also use function objects. Commented Aug 26, 2022 at 15:33
  • 1
    wandbox.org/permlink/S3sBz33LmrFNKWRz Commented Aug 26, 2022 at 15:43
  • 2
    I disagree with the duplicate. OP appears to be trying to populate 20 different functors in a loop where the body depends on the loop index. This is clearly a use case for lambdas, not for clarification on array of function syntax. Commented Aug 26, 2022 at 15:47

4 Answers 4

6
   f[i]= -par[i]+x[i]*par[i+1];

You can't generate code at runtime, so you can't do exactly what you're asking for.

You can however save the value of i for use at runtime, so you have a single callable object with a hidden parameter i not passed explicitly by the caller. The simplest example is

auto f = [i](const double *x, const double *par)
         {
            return -par[i]+x[i]*par[i+1];
         };

but this gives a unique type to the lambda f, so you can't easily have an array of them.

You can however write

using Callable = std::function<double, const double *, const double *>;
std::array<Callable, 20> f;

and store the lambdas in that.

I think you'll need to use ROOT::Math::WrappedParamFunction<Callable> for this to work, though, since the FuncPtr parameter type is not erased.


If you really can't change the WrappedParamFunction type parameter for whatever reason, you can generate a free function instead of a stateful lambda using templates - but it's pretty ugly.

Edit - I was considering writing that version out too, but fabian beat me to it. Do note that you have to either write out all that machinery for each distinct function that needs this treatment, wrap it in a macro, or generalize everything to take a function template parameter as well.

Sign up to request clarification or add additional context in comments.

Comments

3

There are almost certainly better ways of accomplishing this, but this probably gets you closest to the desired result described in the question:

Create a function template with the offset as template parameter and then create an std::array of function pointers with function pointers pointing to specializations of a template function. Note that the size of the array must be a compile time constant for this to work:

template<size_t Offset>
double f(const double* y, const double* par)
{
    return -par[Offset] + y[Offset] * par[Offset+1];
}

template<size_t ... Offsets>
std::array<double(*)(double const*, double const*), sizeof...(Offsets)> CreateFsHelper(std::index_sequence<Offsets...>)
{
    return { &f<Offsets>... };
}

template<size_t N>
std::array<double(*)(double const*, double const*), N> CreateFs()
{
    return CreateFsHelper(std::make_index_sequence<N>{});
}

int main()
{
    auto functions = CreateFs<20>();
}

Comments

0

Making your i a template parameter and generating the functions recursively at compile time can also do the trick:

using FunctionPrototype = double(*)(const double *, const double *);

template<int i>
double func(const double * x, const double * par) {
  return -par[i]+x[i]*par[i+1];
}

template<int i>
void generate_rec(FunctionPrototype f[]) {
  f[i-1] = &func<i-1>;
  generate_rec<i-1>(f);
}

template<>
void generate_rec<0>(FunctionPrototype f[]) { }

template<int i>
FunctionPrototype* generate_functions()
{
  FunctionPrototype * f = new FunctionPrototype[i]();
  generate_rec<i>(f);
  return f;
}


FunctionPrototype * myFuncs = generate_functions<3>(); // myFuncs is now an array of 3 functions

Comments

0

"Is there a way to create an array of functions inside a loop in C or C++"

sure, you can create a std::array or std::vector of std::function.

You can also create a container of function pointers if you so desire.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.