Home > Software engineering >  Having a set of regex and a set of functions, how to link each regex to a specific function and then
Having a set of regex and a set of functions, how to link each regex to a specific function and then

Time:06-28

// vector of pairs
vector<pair<regex, void (*)(string)>> patterns;
// store a regex pattern          
patterns[0].first = pattern;
// store a function name
patterns[0].second = func1;
patterns[1].first = pattern2;
patterns[1].second = func2;
// take user's input
string input;
cin >> input;
for (int i = 0; i < patterns.size(); i  )
{
    if (regex_match(input, patterns[i].first)) // if the input matches one of the patterns, call the function associated with that pattern
    {
        patterns[i].second(input);
    }
}

The problem is that I'm getting an error message for patterns[i].second(input); saying that: the expression preceding parentheses of apparent call must have (pointer-to-) function type

CodePudding user response:

I would do it this way, so that the code also becomes a bit more self explaining :

#include <iostream>
#include <vector>
#include <string>
#include <regex>
#include <functional>

// using namespace std; <== NO

using handler_function_t = std::function<void(const std::string&)>;

// do not use pair, so hard to reason back to WHAT you mean
// define a struct 
struct pattern_handler_t
{
    pattern_handler_t(std::string&& rx, handler_function_t&& fn) :
        regular_expression{ rx },
        call{ fn }
    {
    };

    const std::regex regular_expression; // give clear readable names, so much better then 'first' of pair
    const std::function<void(const std::string&)> call;
};

void callback1(const std::string& text)
{
    std::cout << text << ", starts with an a\n";
}

void callback2(const std::string& text)
{
    std::cout << text << ", starts with a b\n";
}

int main()
{
    // you can use an initializer list.
    std::vector<pattern_handler_t> pattern_handlers
    {
        {"a.*",callback1},
        {"b.*",callback2}
    };

    std::string input{ "another example" };
    //std::cin >> input;

    // use range based for loop for vector
    for (const auto& pattern_handler : pattern_handlers)
    {
        std::smatch match;
        if (std::regex_match(input, match, pattern_handler.regular_expression))
        {
            pattern_handler.call(match[0]);
        }
    }

    return 0;
}

CodePudding user response:

Let us first look at your code: I will show you the problems and how to fix it. And then I will show, how it could be optimized further.

I will put comments in your code to show the problems:

// vector of pairs

// *** Here we have the base for a later problem
// *** After this line, you will have an empty vector, that has no elements.

vector<pair<regex, void (*)(string)>> patterns;

// *** The vector is empty.
// *** And if you try to access patterns[0] or patterns[1]
// *** then you try to acess elements that don't exist.
// *** This will cause UB (Undefined Behaviour) 

// store a regex pattern patterns         
patterns[0].first = pattern;
// store a function name
patterns[0].second = func1;
patterns[1].first = pattern2;
patterns[1].second = func2;

// *** all UB (Undefined Behaviour) from here on.

// take user's input

//*** Please use getline to read a complete line

string input;
cin >> input;
for (int i = 0; i < patterns.size(); i  )
{
    if (regex_match(input, patterns[i].first)) // if the input matches one of the patterns, call the function associated with that pattern
    {
        patterns[i].second(input);
    }
}

So, obviously, you want to have a std::vector with 2 elements, so that you can use the index operator to fill it.

With that the easiest way will be to use std::vectors constructor no. 4 to set aninitial size. This could be done like this:

std::vector <std::pair <std::regex, void (*)(std::string&)>> patterns(2);

Then your could use the index operator [] to assign values.

But most likely, you would use either the std::vectors push_back - function, or an initializer list.

So, an intermediate optimized solution could look like:

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <regex>

void function1(std::string& s) {
    std::cout << "Function 1:\t " << s << '\n';
}
void function2(std::string& s) {
    std::cout << "Function 2:\t " << s << '\n';
}

int main() {
    // Pattern and related callback function
    std::vector <std::pair <std::regex, void (*)(std::string&)>> patterns{};

    // Initialize vector
    patterns.push_back({ std::regex(".*abc.*"),function1 });
    patterns.push_back({ std::regex(".*def.*"),function2 });

    // Get input from user
    std::string input{};
    std::getline(std::cin, input);

    // Find a pattern and call the corresponding function
    for (size_t i{}; i < patterns.size();   i) {

        if (std::regex_match(input, patterns[i].first))
            patterns[i].second(input);
    }
}

Modern C has more nice features, like structured bindings and range based for loops.

With that, the code can be made even more readable.

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <regex>

void function1(std::string& s) {
    std::cout << "Function 1:\t " << s << '\n';
}
void function2(std::string& s) {
    std::cout << "Function 2:\t " << s << '\n';
}

int main() {
    // Pattern and related callback function
    std::vector <std::pair <std::regex, void (*)(std::string&)>> patterns{
        { std::regex(".*abc.*"),function1 },
        { std::regex(".*def.*"),function2 }
    };

    // Get input from user
    std::string input{};
    std::getline(std::cin, input);

    // Find a pattern and call the corresponding function
    for (const auto& [regex, function] : patterns) {

        if (std::regex_match(input, regex))
            function(input);
    }
}
  • Related