1

I am writing a program that deals with reading a text file.

Based on the starting keyword of each line in a file, i want to call a different function.

void funnel(string line) {
    if (line.find("Keyword1", 0) == 0){
        call_function1();
    }
    else if (line.find("Keyword2", 0) == 0){
        call_function2();
    }
    else if (line.find("Keyword3", 0) == 0){
        call_function3();
    }
 ............
}

Is there a simpler/better/more efficient way to do this or am i stuck with this void funnel thing?

ClientConnect: (blake_won) ID: 0 (IP: 192.168.0.177:29071)
Kill: 0 0 7: blake_won killed blake_two by MOD_SWORD

The lines look something like this, and i want to call an appropriate function based on each starting keyword. The file is not my own so i don't have control over what's written to it.

blake_won
  • 29
  • 3
  • 4
    a `std::map` of `std::string` to `std::function` will probably simplify it – Alan Birtles Nov 03 '20 at 12:25
  • "s there a simpler/better/more efficient way to do this" - perhaps using `antlr`? but we need **far more context** than you've posted. What are you parsing? What place in the [_Chomsky hierarchy_](https://en.wikipedia.org/wiki/Chomsky_hierarchy) is the language/grammar you're parsing? Etc – Dai Nov 03 '20 at 12:27
  • 2
    And wtf is a "funnel"?! That's not a term used in any formal-grammar system I'm aware of. There's nothing wrong with creating your own terms, but when you do **you must define them as stringently as possible** so that the rest of us cab understand what you're trying to accomplish. – Dai Nov 03 '20 at 12:27
  • 2
    Sounds like you are parsing a file that contains a grammar. You may find the C++ [Spirit](https://en.wikipedia.org/wiki/Spirit_Parser_Framework) parser framework to be very helpful for that kind of problem domain. It's suitable for grammars both large and small, but has a bit of a learning curve. – Eljay Nov 03 '20 at 12:30
  • 1
    You can [edit] your question if you want to add information to it. Will make it clearer for whoever reads it in future. – anatolyg Nov 03 '20 at 12:37
  • Edited the question with more info. – blake_won Nov 03 '20 at 12:39
  • 2
    @Dai This is a [funnel](https://www.eis.co.za/image/cache/catalog/CHAIN/Product%20Image/FUNPM-800x800.jpg) and the term is usually used when you are collecting a number of things and gathering them to a single place or through some common place/area. I would say the analogy is very clear. – super Nov 03 '20 at 12:42
  • @super Just that it is used in reverse, you put single lines, one after the other, into the narrow end and then one of several places to process it is selected from the wide end.... – Yunnosch Nov 03 '20 at 12:46
  • 2
    @Yunnosch No, you put all the string representations into the funnel, that funnels it through the part of the code with all the `if` statements. – super Nov 03 '20 at 12:48
  • may be you also use switch case like in this [example](https://stackoverflow.com/questions/41337256/using-strings-in-switch-statements-where-do-we-stand-with-c17) – gowridev Nov 03 '20 at 14:22

3 Answers3

2

I am writing a program that deals with reading a text file.

Given your example, I would at least consider embedding some existing interpreter inside your program, such as Lua or Guile (or maybe Python).

The advantage of reusing an existing interpreter are both for you as a developer and your users.

  • as a developer, you are reusing some mature open source software, and saving precious developer efforts.

  • the users of your software could refer to existing documentation and examples.

Be aware that embedding some interpreter is a major software architectural decision.

The interpreter imposes some coding rules (in particular, related to memory management and garbage collection).

Alternatively, read more about parsing (e.g. the first half of the Dragon book), and use some parser generator, such as GNU bison or ANTLR. Be sure to document first (in EBNF notation) the syntax of your valid input.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

A regular expression can really help gathering information from a uniform pattern like this:

std::regex pattern { R"(^(\w+):)" };
std::map<std::string, std::function<void()>> dispatch {
    { "function1", function1 },
    { "function2", function2 }
};

void funnel(const std::string& line) {
     std::smatch match;
     if (std::regex_search(line, match, pattern) && match.size() > 1) {
        auto command = match.str(1);
        auto fn = dispatch.find(command);
        if (fn != dispatch.end()) {
            fn->second();
        } else {
            std::cout << "command not recognized" << std::endl;
        }
     } else {
         std::cout << "pattern doesn't match";
     }
}

Demo: https://godbolt.org/z/s5Mqej

parktomatomi
  • 3,851
  • 1
  • 14
  • 18
1

If you have repetitive code like this, stuff your data into a vector and iterate over it.

For example: vector<pair<string, function<void()>>>. Or, in human language: each element in the list is a pair, whose first element is your keyword, and the second element is the function you want to call. You can replace pair by a dedicated struct, if you want to make it more explicit.

    void funnel(string line, const vector<pair<string, function<void()>>>& list)
    {
        for (auto element: list)
        {
            if (line.find(element.first, 0) != string::npos) // search
            {
                element.second(); // call the function
                return;
            }
        }
    }
anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • Out of the submitted suggestions this was the cleanest so I'm going for this one. Thanks for everyone that tried to help! – blake_won Nov 03 '20 at 14:58