Thread: Map of functions, to call only those functions whose names are written in a disk file

  1. #1
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81

    Map of functions, to call only those functions whose names are written in a disk file

    Here is a working C++ program and its output that reads the names of a certain number of functions whose names are written in a disk file that provides the control parameters in a near-real time fashion. Based on this information, the program calls those functions whose names were given in this text file as parameters. The method I have used is to build a map of functions that connects the names of the functions to the actual functions. Once this map is created, then it is very easy to iterate in such a way that only those selected functions are called.

    So far this is working well, and it makes the logic of the code easier because ultimately there will be a large inventory of functions (probably hundreds of functions), and this way of pre-building a map of functions avoids writing hundreds of if statements in the code. Of course, pre-building this reusable map in the background is still equivalent to writing that many if statements, but at least it makes the code very compact and much more automatic, since it is done only once.

    But I now have a related question: In this case (when the selected subset of functions to call is given as incoming data as in this example), does C++ make it possible to avoid using maps of functions, by directly applying the string variable that holds the name of the function in order to call the original function immediately For instance, if a string variable s = "MyFunction", then is there a more direct way of using this string as if it were the actual function whose name is spelled exactly in that way? This would make the code even more compact.

    MANY THANKS!

    Anyway, here is the sample test code for the map of functions, and its output (the real-world version of the code is already very helpful already):

    First of all here is the text file "FileForNamesOfFunctions.TXT" that contains the part of the "data", which is the set of functions select from from the inventory and only call these:
    Code:
    function2
    function3
    function5
    And here is the C++ code that uses this "data":

    Code:
    
    #include <fstream>
    #include <iostream>
    #include <sstream>
    #include <string>
    
    #include <vector>
    #include <map>
    
    using namespace std;
    
    void TestChosenFunctions(); 
    void SetUpMapOfFunctionsToActualFunctions();
    void GetControlFunctionNamesFromDiskFile();
    
    
    void function0(double);
    void function1(double);
    void function2(double);
    void function3(double);
    void function4(double);
    void function5(double);
    
    // At this stage I have made the following variables (the vector and map)
    // global to make  this test code  easier to write, but in the real world
    // it is possible to make sure that these variables are within the
    // main function and that they are passed as arguments to functions: 
    vector<string> vec_NamesOfFunctions;
    map<string, void (*)(double) > Map_NamesOfFunctionsToActualFunctions;
    
    
    
    
    int main()
    {
         TestChosenFunctions( ); 
    }
    
    
    void TestChosenFunctions( )
    {                     
        
    
    
        SetUpMapOfFunctionsToActualFunctions();
    
    
        GetControlFunctionNamesFromDiskFile();
    
    
        double x=1.0;
        cout << "Here x = 1.0" << endl;
        cout << "We selectively call only those functions whose names were written in the text file:" << endl;
        for( auto itr=vec_NamesOfFunctions.begin(), end=vec_NamesOfFunctions.end() ; itr != end; ++itr)
        { 
             if( Map_NamesOfFunctionsToActualFunctions.count(*itr) != 0 )
             {
                  Map_NamesOfFunctionsToActualFunctions[ *itr ]( x )  ;
             }
             else
            {
                 cout << "Error: Function name missing from the map!" << endl;
            }
        }
        cout << "------------------------------------" << endl;
    
    
     }
    
    
     void GetControlFunctionNamesFromDiskFile(   )
    {
          string ControlParamFileFunctionsForFunctions = "FileForNamesOfFunctions.TXT";
          istringstream instream;   // Declare an input string stream
          ifstream inFile;
          string  firstStringOfLine;
          string s;
    
    
          inFile.open( ControlParamFileFunctionsForFunctions.c_str(), ios::in);
          if ( ! inFile.is_open() )
          {
                cout << "Could not open Key Text Input file!" << endl;
               exit(1);
          }
          while(  getline(inFile, s)  )
          {  
               instream.clear();
               instream.str(s);        
               instream >> firstStringOfLine;
              vec_NamesOfFunctions.push_back(firstStringOfLine);
          }
        
         inFile.close(); 
         cout << "The function names written in the disk file are: " << endl; 
         for( auto itr=vec_NamesOfFunctions.begin(), end =vec_NamesOfFunctions.end(); itr != end;  ++itr)
         {
              cout<<  *itr << endl; ;
          }
         cout << "---------------------------------------------" << endl; 
    
    
    }
    
    
    void function0(double x) {cout<<"function0 prints 0*x = " << 0*x << endl; }
    void function1(double x) {cout<<"function1 prints 1*x = " << 1*x << endl; }
    void function2(double x) {cout<<"function2 prints 2*x = " << 2*x << endl; }
    void function3(double x) {cout<<"function3 prints 3*x = " << 3*x << endl; }
    void function4(double x) {cout<<"function4 prints 4*x = " << 4*x << endl; }
    void function5(double x) {cout<<"function5 prints 5*x = " << 5*x << endl; }
    
    
    void SetUpMapOfFunctionsToActualFunctions()
    {
        Map_NamesOfFunctionsToActualFunctions["function0"]=function0;
        Map_NamesOfFunctionsToActualFunctions["function1"]=function1;
        Map_NamesOfFunctionsToActualFunctions["function2"]=function2;
        Map_NamesOfFunctionsToActualFunctions["function3"]=function3;
        Map_NamesOfFunctionsToActualFunctions["function4"]=function4;
        Map_NamesOfFunctionsToActualFunctions["function5"]=function5;
    }
    And finally here is the output of the program:
    ===========================
    The function names written in the disk file are:
    function2
    function3
    function5
    ----------------------------------------------------
    Here x = 1.0
    We selectively call only those functions whose names were written in the text file:
    function2 prints 2*x = 2
    function3 prints 3*x = 3
    function5 prints 5*x = 5
    --------------------------------------------------
    Last edited by FortranLevelC++; 05-26-2013 at 03:03 AM.

  2. #2
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    If there were a way of doing it, then "behind the scenes" it would be basically the same as what you are doing.
    This is essentially what games that allow binding a key to a command via a config file (like Quake) do.

    Two things you can do:
    1. If you have enough functions, then using an unordered_map would be a little faster.
    2. Some macro trickery can be used to register the function into the map automatically. You have to be careful to avoid the static initialisation order fiasco though. Something like this:
    Code:
    #include <map>
    #include <string>
    #include <iostream>
    
    typedef void (*mappedMethod)(double);
    typedef std::map<std::string, mappedMethod> methodMap;
    methodMap Map_NamesOfFunctionsToActualFunctions;
    
    void registerMethod(std::string methodName, mappedMethod method)
    {
        static std::map<std::string, mappedMethod> myMethods;
        if (method != NULL)
            myMethods[methodName] = method;
        else
            Map_NamesOfFunctionsToActualFunctions.swap(myMethods);
    }
    #define STRING(x) #x
    #define MAP_THIS_METHOD(x) \
        void x(double); \
        struct x##_type { \
            x##_type() { \
                registerMethod(STRING(x), &x); \
            } \
        } x##_var; \
        void x
    
    using namespace std;
    
    MAP_THIS_METHOD(function0)(double x) {cout<<"function0 prints 0*x = " << 0*x << endl; }
    MAP_THIS_METHOD(function1)(double x) {cout<<"function1 prints 1*x = " << 1*x << endl; }
    MAP_THIS_METHOD(function2)(double x) {cout<<"function2 prints 2*x = " << 2*x << endl; }
    
    int main()
    {
        // This safely gets the methods, avoiding the static initialisation order fiasco.
        // No need for manually registering the functions.
        registerMethod("", NULL);
    
        // Essentially call
        // Map_NamesOfFunctionsToActualFunctions["function1"](42.0);
        // but only if it exists...
        methodMap::iterator m = Map_NamesOfFunctionsToActualFunctions.find("function1");
        if (m != Map_NamesOfFunctionsToActualFunctions.end())
            m->second(42.0);
    
        return EXIT_SUCCESS;
    }
    This was adapted from my unit test framework.
    Last edited by iMalc; 05-26-2013 at 03:41 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  3. #3
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Thanks for the detailed answer, iMalc These macros are very useful, and I can immediately incorporate these ideas, but since I know almost nothing about macro programming, is there a recommended book that I can read to learn this subject?

  4. #4
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    I don't know of any good books on it, but this seems to cover what I've used here very well, and more:
    Tutorials - C Preprocessor Tricks - Cprogramming.com
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  5. #5
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Also take a look at lambda functions:
    Code:
    #include<iostream>
    #include<functional>
    
    std::function<void(double)> makeFunction(double y)
    {
        return [=](double x){std::cout<<"function "<< y<<" prints "<<y<<"*"<<x<<" = "<< y*x <<std::endl;};
    }
    int main()
    {
        //example 1
        auto foo = makeFunction(5);
        foo(4);
        
        //example 2
        makeFunction(1)(6);
    }
    Make sure you understand that a function is literally created by the other function, which is sometimes called a higher-order function.

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    MAP_THIS_METHOD
    O_o

    I hate that you are calling this "METHOD". ;_;

    *shrug*

    Anyway, you are already using the direct "Construct on First Use" idiom without returning a reference to the object so I can't help but wonder why your implementation forcefully introduces the unprotected global.

    I realize, of course, that the use here is the documented case, but still the `map_NamesOfFunctionsToActualFunctions' variables is unprotected when it could trivially be a local object or similar which seems to be the intent.

    Soma

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by phantomotap View Post
    I hate that you are calling this "METHOD"
    Actually, after I came back and saw what I had posted later on, that was the first thing I noticed and wished I could change.
    Anyway, you are already using the direct "Construct on First Use" idiom without returning a reference to the object so I can't help but wonder why your implementation forcefully introduces the unprotected global.

    I realize, of course, that the use here is the documented case, but still the `map_NamesOfFunctionsToActualFunctions' variables is unprotected when it could trivially be a local object or similar which seems to be the intent.
    This was just the first way I thought of (quite some time ago) which was actually guaranteed to work.

    Edit: Oh I know why I hadn't done that. I actually keep a vector of the method names as well in my own code, to shuffle and perform the tests in random order, and since I can't return two things so I returned neither. I'll change it to build the vector later though and take the names from the map instead.
    Last edited by iMalc; 05-27-2013 at 01:21 PM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by manasij7479 View Post
    Also take a look at lambda functions:
    Code:
    #include<iostream>
    #include<functional>
    
    std::function<void(double)> makeFunction(double y)
    {
        return [=](double x){std::cout<<"function "<< y<<" prints "<<y<<"*"<<x<<" = "<< y*x <<std::endl;};
    }
    int main()
    {
        //example 1
        auto foo = makeFunction(5);
        foo(4);
        
        //example 2
        makeFunction(1)(6);
    }
    Make sure you understand that a function is literally created by the other function, which is sometimes called a higher-order function.

    Many thanks, Manasij. Lambda functions are still too advanced for me, I will have to study this in detail. As far as I can understand, the function that you wrote above is basically generating code, but how can I use this kind of lambda programming to use the string variable that contains the name of a function to call the actual real function that this name is referring to? If this lambda code can be used as an alternative to the macros written by iMalc, then can you give some more examples to call an existing function as a reaction to a string variable that is in fact a message to call that function?
    Last edited by FortranLevelC++; 05-27-2013 at 06:18 PM.

  9. #9
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    As far as I can understand, the function that you wrote above is basically generating code
    O_o

    Well, that is right, but I think you got there for the wrong reasons.

    In fairness to you, "a function is literally created by the other function" is also wrong.

    The code uses generative facilities in that `std::bind' and `std::function' use generative facilities, but there is no generative code in the example.

    [Edit]
    Well, I guess you could argue that the particular flavor of class, similar in nature to `STest', return by the `makeFunction' is generated, but that happens during compilation, and the generation only happens once.
    [/Edit]

    That code simply binds a value as a parameter to an existing function which just happens to be an anonymous function.

    You can see how this works out in my example code (#1).

    Let me explain, the anonymous function within `makeFunction' isn't created on the fly during program execution; the function exists during compilation and is only bound to values during program execution. Basically, the compiler creates a function of the `void(double, double);' variety and binds the first parameter and a pointer to that function within a class which then calls that function with the stored parameter on demand.

    If it helps, you can think of that code as doing the same as my example code (#2).

    The higher level facilities such as `std::bind' and `std::function' just make this code a lot simpler.

    The use of the lambda function just prevents you from having to jump through hoops such as creating a named function.

    how can I use this kind of lambda programming to use the string variable that contains the name of a function to call actual real function that this name is referring to?
    You can't. Well, you can't without also using something like iMalc posted or doing it manually.

    As weird as it may seem, the C/C++ compiler doesn't know the names of functions as strings. Even the processor directive `__FUNCTION__' isn't guaranteed to be meaningful.

    *shrug*

    I think the point was more not separating the naming from the mapping.

    Code:
    my_map["func_name"] = [=](double x){std::cout<<"function "<<x<<std::endl;}; // or similar
    Soma

    Code:
    #include <functional>
    #include <iostream>
    
    void Go
    (
        double f1
      , double f2
    )
    {
        using namespace std;
        cout << "function " << f1 << " prints " << f2 << "*" << f1 << " = " << f2 * f1 << endl;
    }
    
    std::function<void(double)> makeFunction
    (
        double f1
    )
    {
        using namespace std;
        return(bind(&Go, f1, placeholders::_1));
    }
    
    int main()
    {
        std::function<void(double)> s1(makeFunction(5));
        s1(4);
        makeFunction(1)(6);
        return(0);
    }
    Code:
    #include <iostream>
    
    struct STest
    {
        STest
        (
            double f
        ):
            m(f)
        {
        }
        double operator ()
        (
            double f
        )
        {
            using namespace std;
            cout << "function " << m << " prints " << f << "*" << m << " = " << f * m << endl;
        }
        double m;
    };
    
    int main()
    {
        STest s1(5);
        s1(4);
        STest(6)(1);
        return(0);
    }
    Last edited by phantomotap; 05-27-2013 at 06:51 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. names of user defined functions
    By jackson6612 in forum C++ Programming
    Replies: 5
    Last Post: 05-17-2011, 02:11 PM
  2. Disk access functions
    By Sly in forum C Programming
    Replies: 15
    Last Post: 01-18-2009, 06:27 PM
  3. Method/Functions Names Visibility with SoftIce
    By Davros in forum A Brief History of Cprogramming.com
    Replies: 1
    Last Post: 03-19-2004, 01:23 PM
  4. how to call a DLL functions in VC++
    By mjmariveles in forum Windows Programming
    Replies: 4
    Last Post: 07-08-2002, 08:57 PM
  5. Calling functions written in assembly?
    By The V. in forum C Programming
    Replies: 5
    Last Post: 10-24-2001, 08:11 PM