2

Consider the following function:

template <int node>
void RemainingEnergyTrace (double oldValue, double newValue)
{
  std::stringstream ss;
  ss << "Energy_" << node << ".log";
  static std::fstream f (ss.str().c_str(), std::ios::out);
  f << Simulator::Now().GetSeconds() << "  Remaining energy=" << newValue << std::endl;
}

Note the template definition of the function int node. I try to pass the address of this function in main():

int inc = 0;  
eSources.Get (inc)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<inc>));

which generates the following errors:

error: the value of ‘inc’ is not usable in a constant expression
eSources.Get (inc)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<inc>));
                                                                                                      ^
error: no matching function for call to ‘MakeCallback(<unresolved overloaded function type>)’
eSources.Get (inc)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<inc>));
                                                                                                          ^

However, the following statement works:

eSources.Get (0)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<0>));

In summary, an actual number works, but when an integer variable is passed in the template format, it does not. Is it because the integer variable has to be of const type (as suggested by the error)?

I am trying to actually run a loop and pass the address of the function for different integer values. How can I make this work?

for(int inc = 0; inc<nWifi; inc++)
{
  eSources.Get (inc)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<inc>));
}
V-Red
  • 239
  • 2
  • 17

1 Answers1

3

In short, you can't. Since templates are expanded at compilation time, you need to provide the values at compilation time as well. If nWifi is a value that's only available at runtime, you need to use a regular parameter:

void RemainingEnergyTrace (double oldValue, double newValue, int node);

If you want then to create partially applied functions to pass to your MakeCallback, you can create them using lambdas:

for(int inc = 0; inc<nWifi; inc++)
{
    auto callback = [=](double oldVal, double newVal) {
        RemainingEnergyTrace(oldVal, newVal, inc);
    };
    ...
}

But this won't decay to a function pointer, so you might need to change your MakeCallback API to e.g. use std::function (which is a preferred method nowadays) or to take an additional parameter1, or alternatively use some library that will provide the delegate functionality you need.


1Typically, a C or C++ API that takes function pointers would also take an additional void* parameter to store alongside the function pointer. Then, when calling, the pointer would be passed to that function, and store the necessary closure data (in your case, it could point to the inc value residing somewhere in memory). Without knowing how the MakeCallback works, it's impossible to tell what would be the best solution here.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135