1

Please, tell me is it safe to call math functions the following way:

map<string,double(*)<double> func_map = { {"sin", &std::sin } ... }
...
double arg = 2.9;
double res = func_map["sin"](arg);
Maxim
  • 13
  • 6
  • Why call them that way? – Michael Chourdakis May 16 '22 at 20:18
  • I'm using RPN to parse math expressions (that could contain math functions as well). Using map to access these functions is much shorter than using lots of "if .. else if ...else if". – Maxim May 16 '22 at 20:20
  • 1
    Be careful: It looks to me like this is [undefined behavior](https://en.cppreference.com/w/cpp/language/extending_std#Addressing_restriction) in C++20. – Nathan Pierson May 16 '22 at 20:24
  • Though declaration of your map is not correct, the function pointer declaration should look like `double(*)(double)` – Aconcagua May 16 '22 at 20:25
  • @NathanPierson The changes in C++20 make the effects *unspecified* or (in some cases) ill-formed - not undefined. – Peter May 17 '22 at 03:27

1 Answers1

6

Taking the addresses of functions in the standard library not on the Designated addressable functions list leads to unspecified behavior (since at least C++20). std::sin and the other <cmath> functions are not on that list so, to be safe, wrap them up in functors, like lambdas:

#include <cmath>
#include <map>
#include <string>

int main() {
    std::map<std::string, double(*)(double)> func_map = {
        {"sin", [](double x) { return std::sin(x); }},
        {"cos", [](double x) { return std::cos(x); }},
    };
}

is it safe to call math functions the following way:

double res = func_map["sin"](arg);

No, if the function you aim to call is not present in func_map, using the subscript operator[] would first insert a double(*)(double) pointing at nullptr into the map and then return that nullptr. Calling nullptr(arg) would lead to undefined behavior. To make it safe you can do a couple of things:

  • Make func_map const. This prevents you from using any functions potentially inserting something in the map, like the subscript operator.
  • Use func_map.at("sin")(arg); to get an exception (std::out_of_range) if the function doesn't exist in the map. You can safely catch that and print a message to the user:
    try {
        double res = func_map.at("sin")(arg);
        std::cout << res << '\n';
    } catch (const std::out_of_range& ex) {
        std::cout << "unknown function\n";
    }
    
  • If you don't want exceptions for unknown functions, you can use the member function find instead:
    if(auto fit = func_map.find("sin"); fit != func_map.end()) {
        double res = fit->second(arg);
        std::cout << res << '\n';
    } else {
        std::cout << "unknown function\n";
    }
    
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108