Home > Blockchain >  Failure in template type resolution
Failure in template type resolution

Time:12-05

This is driving me insane. I just want to send a variadic list of pairs of strings into a template function that replaces words in a string, and the function works, but only with one parameter, not variadic.

This works fine

using std::string;
using spair = std::pair<string, string>;


string replace_all(string& str, const spair& sp)
 {
    string::size_type pos = 0;
    string::size_type rmsize = std::get<0>(sp).size();

  while((pos = str.find(std::get<0>(sp), pos)) != string::npos)
     {
        str.replace(pos, rmsize, std::get<1>(sp));
        pos  ;
     }

  return str;

}

string pattern = "My name is {{first_name}} {{last_name}} and I live in {{location}}";

string t = replace_all( pattern,  {"{{first_name}}","Homer"}  );

/*************************/

But this will not work

template<typename T=spair, typename  ...Args>
string replace_all(string& s, const T& arg1,  Args const&... args )
{
   s = replace_all(s, arg1);
  
   s = replace_all(s, args...);

  return s;  
}

string v = replace_all( pattern,         
          {"{{first_name}}","Homer"},
          {"{{last_name}}","Simpson"}, 
          {"{{location}}","Springfield"} 
          );

variadic.cpp:72:8: error: no matching function for call to **‘replace_all(std::string&, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>)’

CodePudding user response:

There might be some abuse of initializer lists and templates here. To sum up:

  • The internal type is known, it's always a pair of strings.
  • The amount of "replace pairs" should be configurable

Using templates here, would force a new (function) type to be generated whenever replace_all is called with a different number of pairs. You can greatly simplify if you rely on "runtime lists" like so:

string replace_all(string& s, std::vector<spair> const& v)
{
    for (auto const& sp : v) replace_all(s, sp);
    return s;  
}

// call it like so
replace_all(pattern, 
  {         
    {"{{first_name}}","Homer"},
    {"{{last_name}}","Simpson"}, 
    {"{{location}}","Springfield"} 
  }
) << std::endl;

Demo

As @Sam Varshavchik mentions in the comments, doing pattern matching with the method you suggest is a bit of a problem. It could work if you call your function like so:

replace_all(pattern,        
    spair{"{{first_name}}","Homer"},
    spair{"{{last_name}}","Simpson"}, 
    spair{"{{location}}","Springfield"} 
);

thus making the type to be deduced explicit.

CodePudding user response:

I could not get it exactly, but I devised a workaround:

// 1 to 5 replacement pairs
const spair emptyspair{{},{}};  // A "nothing" pair of empty strings

string replace_all(string& s,
           const spair& sp1=emptyspair,
           const spair& sp2=emptyspair,
           const spair& sp3=emptyspair,
           const spair& sp4=emptyspair,
           const spair& sp5=emptyspair
           )
{
  
  std::array<spair,5> v{sp1, sp2, sp3, sp4, sp5};
  
  for (spair sp : v )
    {
      if (sp != emptyspair)
         {
            auto& s1 = std::get<0>(sp); 
            auto& s2 = std::get<1>(sp);
            s = replace_all(s, s1, s2);
         }
    }
  
  return s;
}

This allows 1 to 5 spair arguments, defaulting to a dummy pair each that are ignored in the array that holds them. I think that all references bind to the same default object so do not cause 10 string objects and 5 pairs to be created, and the inequality check compares quickly to the same object.

  • Related