vendredi 7 août 2020

Get a non-const iterator from a const string parameter

I am reading strings with the following syntax:

string example {"firstList:[element1:value1,element2:value2];secondList:[elementA:valueA,elementB:valueB]"};

Since a real list can have hundreds of elements, and I need only read operations (checking if value1 == "something" or if elementA exists), I implemented a std::string::iterator based function to find a list inside a given string. Here is a simplified version:

bool findList(
    const std::string &input,
    const std::string &listName,
    std::string::iterator globalStart,
    std::string::iterator globalEnd,
    std::string::iterator &listStart,
    std::string::iterator &listEnd
    )
{
    if (input.empty() || listName.empty()) return false;

    size_t inicio = input.find(listName);
    if (inicio == string::npos) return false;

    size_t fin = input.find("]", inicio);
    if (fin == string::npos) return false;

    listStart = globalStart + inicio + listName.length() + 2;
    listEnd = globalStart + fin;
    if (listEnd >= globalEnd) return false;

    string::iterator badList = std::find(listStart, listEnd, ';');
    if (badList != listEnd) return false;

    return true;
}

The function works, but I have to pass the string input, input.begin(), and input.end() to assign values for the list iterators. Is there another option to assign the input.begin() to listStart (and the same for listEnd)?

Writing:

    listStart = input.begin() + inicio + listName.length() + 2;
    listEnd = input.begin() + fin;

yields a compiler error for trying to assign a const iterator to a non-const iterator:

error: no match for ‘operator=’ (operand types are ‘std::__cxx11::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >}’ and ‘__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >’)
  listStart = input.begin() + inicio + listName.length() + 2;

I am aware that passing the input only as std::string &input solves the problem, but I would like to keep the checks that const entails.

Here is a minimal example:


#include <iostream>
#include <string>
#include <algorithm>
#include <assert.h>

using std::cout;
using std::endl;
using std::string;
using std::size_t;

bool findList(
    const std::string &input,
    const std::string &listName,
    std::string::iterator globalStart,
    std::string::iterator globalEnd,
    std::string::iterator &listStart,
    std::string::iterator &listEnd
    )
{
    if (input.empty() || listName.empty()) return false;

    size_t inicio = input.find(listName);
    if (inicio == string::npos) return false;

    size_t fin = input.find("]", inicio);
    if (fin == string::npos) return false;

    listStart = globalStart + inicio + listName.length() + 2;
    listEnd = globalStart + fin;
    if (listEnd >= globalEnd) return false;

    string::iterator badList = std::find(listStart, listEnd, ';');
    if (badList != listEnd) return false;

    return true;
}

int main() {
    string example {"firstList:[element1:value1,element2:value2];secondList:[elementA:valueA,elementB:valueB]"};
    string::iterator listStart, listEnd;
    bool answer = false;

    // Test the findList function
    answer = findList(example, "firstList", example.begin(), example.end(), listStart, listEnd);
    assert(answer == true);
    assert(string(listStart, listEnd) == "element1:value1,element2:value2");
    cout << "Test firstList passed" << endl;

    answer = findList(example, "secondList", example.begin(), example.end(), listStart, listEnd);
    assert(answer == true);
    assert(string(listStart, listEnd) == "elementA:valueA,elementB:valueB");
    cout << "Test secondList passed" << endl;

    answer = findList(example, "thirdList", example.begin(), example.end(), listStart, listEnd);
    assert(answer == false);
    assert(string(listStart, listEnd) == "elementA:valueA,elementB:valueB"); // iterators remain unchanged
    cout << "Test thirdList passed" << endl;

    return 0;
}

*This is my first question, please feel free to point out any improvement I could make to the post.

Aucun commentaire:

Enregistrer un commentaire