vendredi 16 juin 2023

how to avoid implicit conversion of arguments leading to infinite recursion?

I have a formatting method that I adapted from this example.

I have reduced it to just function calls and a print

It works when the formatting string (first argument) is a const char *

#include <stdio.h>
#include <string>

// base
std::string format(const char *s)
{
    printf("** %s\n",s);
    return "";
}
// recursive, should parse each argument and print the formatted string
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
    printf("** %s\n",s);  // dummy
    return "";
}

int main()
{
   format("foo");   
   printf("yay!\n");
}

now I'd like to pass a std::string to format, I have to do:

std::string s = "foo";
format(s.c_str());

I'd like to do

format(s);

So I have added this

// recursive
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
  return format(s.c_str(), args...);
}

But when I pass the string directly as std::string it crashes. Debugging shows an infinite recursion. Difficult to debug templates with std::string constructions, but my guess is that

return format(s.c_str(), args...);

calls itself, because const char * implicitly converts as a std::string.

Here's the full non-working example:

#include <stdio.h>
#include <string>

// recursive
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
  return format(s.c_str(), args...);
}


// base
std::string format(const char *s)
{
    printf("** %s\n",s);
    return "";
}
// recursive, should parse each argument and print the formatted string
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
    printf("** %s\n",s);  // dummy
    return "";
}

int main()
{
   std::string s = "foo";
   format(s);   
   printf("yay!\n");  // crashes before that
}

I could ditch const char * version altogether and go full std::string but I would like to avoid to build a string when a string literal is passed.

So how to keep the ability to pass either const char * directly or std::string as first argument ?

Aucun commentaire:

Enregistrer un commentaire