Correctly copying temporary string from a string_view (C++ 17)

104 Views Asked by At

I have a function with the following declaration:

void Add(string_view source, string_view target);

What should it do: it adds a pair of words from string_views and saves them for the later usage:

  • User types source word -> target word should be returned
  • User types target word -> source word should be returned

I have a constraint, that I can have only 1 copy of each string. So my idea was to save string_views to maps (map<source string_view, target string_view> and the opposite) and these string_views should be views of the words, saved, for example in vector.

So the idea of void Add(string_view source, string_view target) is the following:

void Add(string_view source, string_view target){

 - save source string and target string to the vector<string> words;
 - create views from these words and save them into map<string_view source, string_view target>
 - create views from these words and save them into map<string_view target, string_view source>

}

From my previous post [https://stackoverflow.com/questions/78230725/translator-exercise-by-using-string-view-c/78230782#78230782][1] I understood, that strings (inserted into the function) are temporary and they got destroyed after they are out of their scope.

My question is: how can I save strings from the string_view (efficiently), such that I can later use string_views on them

EDIT: Assignment

Develop a class called Translator that allows saving bilingual word pairs and then translating words from one language to another using the already added pairs:

class Translator {
public:
  void Add(string_view source, string_view target);
  string_view TranslateForward(string_view source) const;
  string_view TranslateBackward(string_view target) const;;

private:
//
};

More specifically, an object t of type Translator should support the following actions:

  1. Creation using the default constructor. In this case, the translation base is empty.

  2. Adding a bilingual pair: t.Add(source, target). After this call, it is assumed that the word source in language 1 corresponds to the word target in language 2. The strings passed to the Add method may be destroyed before the t object.

  3. Translation from language 1 to language 2: t.TranslateForward(source). If there was a previous call t.Add(source_copy, target) for some string target and a string source_copy equal to source (or being the same), it should return target. If there was no such call, it should return an empty string. If there were multiple such calls, it should return target for the last one.

  4. Translation from language 2 to language 1: t.TranslateBackward(target). If there was a previous call t.Add(source, target_copy) for some string source and a string target_copy equal to target (or being the same), it should return source. If there was no such call, it should return an empty string. If there were multiple such calls, it should return source for the last one.

Constraints:

  • Each string should be stored in the Translator class instance no more than once. If this constraint is violated, you will receive a "Memory limit exceeded" error.

EDIT 2: Just for the record, it's not the university assignment, but Online Course (if it's important). [1]: Translator Exercise by using string_view (c++)

1

There are 1 best solutions below

6
Caleth On BEST ANSWER

Read the constraint carefully.

Each string should be stored in the Translator class instance no more than once.

You can use std::string within Translator, and because the strings passed to Add might not outlive t, you must.

We'll use a vector of pairs, to hold each string only once. As the requirements don't specify complexity requirements, we can just linearly search in each direction.

class Translator {
public:
  void Add(string_view source, string_view target);
  string_view TranslateForward(string_view source) const;
  string_view TranslateBackward(string_view target) const;

private:
  void EraseIf(auto pred);
  auto MatchesSource(string_view source) const;
  auto MatchesTarget(string_view target) const;

  std::vector<std::pair<std::string, std::string>> translations;
  static const std::string empty;
};

const std::string Translator::empty;

void Translator::EraseIf(auto pred) 
{
    translations.erase(std::remove_if(translations.begin(), translations.end(), pred), translations.end());
}

auto Translator::MatchesSource(string_view source) const 
{ 
    return [source](auto & translation) { return translation.first == source; }; 
}

auto Translator::MatchesTarget(string_view target) const
{
    return [target](auto & translation) { return translation.second == target; }; 
}

void Translator::Add(string_view source, string_view target)
{
    // Ensure latest is the only translation by removing matches       
    EraseIf(MatchesSource(source));
    EraseIf(MatchesTarget(target));
    // Add the translation
    translations.emplace_back(source, target);
}

string_view Translator::TranslateForward(string_view source) const
{
    if (auto it = std::find_if(translations.begin(), translations.end(), MatchesSource(source)); it != translations.end())
    {
        return it->second;
    }
    return empty;
}

string_view Translator::TranslateBackward(string_view target) const
{
    if (auto it = std::find_if(translations.begin(), translations.end(), MatchesTarget(target)); it != translations.end())
    {
        return it->first;
    }
    return empty;
}