Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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 *

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#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 ?

>Solution :

Your recursive version does not see any declarations to which it could delegate in its return statement.

This is the uppermost declaration and definition you have in your code:

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

It doesn’t "see" any other candidates, thus it will always call itself recursively.

You can fix it by introducing a declaration to your base case above it. Or moving said case there including its implementation:

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

// base
std::string format(const char *s)
{
    printf("** %s\n",s);
    return "";
}

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

// 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
}

Output:

** foo
yay!
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading