Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to fully specialize template for string literals

Tags:

c++

I'm creating my own lexical_cast functions to wrap Boost's, with special behavior for bool types, and also to avoid exception-versions of Boost's lexical cast functions.

I'm fully specializing the functions for bool so that I can use iostreams for the std::boolalpha manipulator. However, I can't get it working for string literals. The full code is below, along with a link to a live sample:

template<typename T1, typename T2>
T1 lexical_cast(T2 const& value, T1 const& defaultValue = T1{})
{
    std::cout << "Generic Conversion\n";
    T1 convertedValue;
    if (!boost::conversion::try_lexical_convert(value, convertedValue))
    {
       return defaultValue;
    }

    return convertedValue;
}

template<>
bool lexical_cast<bool, char const*>(char const* const& value, bool const& defaultValue)
{
    std::cout << "Specialized c string to bool\n";
    bool convertedValue;
    std::istringstream ss(value);
    if (!(ss >> std::boolalpha >> convertedValue))
    {
        std::cout << "Failed string to bool\n";
        return defaultValue;
    }

    return convertedValue;
}

template<>
bool lexical_cast<bool, std::string>(std::string const& value, bool const& defaultValue)
{
    std::cout << "Specialized string to bool\n";
    return lexical_cast<bool>(value.c_str(), defaultValue);
}

template<>
std::string lexical_cast<std::string, bool>(bool const& value, std::string const& defaultValue)
{
    std::cout << "Specialized bool to string\n";
    std::ostringstream ss;
    if (!(ss << std::boolalpha << value))
    {
        std::cout << "Failed bool to string\n";
        return defaultValue;
    }

    return ss.str();
}

int main()
{
    lexical_cast<std::string>(3.14f);
    lexical_cast<float>("3.14");
    lexical_cast<int>("3.14");
    lexical_cast<bool>("true");
    lexical_cast<std::string>(true);
}

Live Sample

The code above gives me the output:

Generic Conversion
Generic Conversion
Generic Conversion
Generic Conversion
Specialized bool to string

The 4th case in the tests in main above should not be "generic conversion", it should be using the C-string specialization.

I feel like I'm going down a rabbit hole of template nastiness here, and the solution is quickly becoming confusing & complex for something so seemingly simple. What is the ideal solution for what I'm trying to do? How do I get bool specialization working as I want?

EDIT

Clarification on requirements: I understand that string literals are actually arrays of characters. In my example above, I tried with char* anyway since accepting an array of char requires another non-type template argument, which I knew right away I couldn't consume since it would require partially specializing my function template, which is illegal.

Secondly, I realize overloads could also be used but I cannot allow cases where lexical_cast can be used without specifying the template parameter for the return type. For example, I have to do lexical_cast<bool>("true"), I can't do lexical_cast("true"). My goal is to remain interface-compatible with boost::lexical_cast, which does not have cases where the template argument can be omitted.

Because I have to use the template syntax when I invoke lexical_cast, I feel like I'm forced to use full function specializations.

like image 807
void.pointer Avatar asked Aug 30 '18 13:08

void.pointer


2 Answers

You have to remember that string literals are really arrays of const characters.

The correct way to have a function accept string literals is like this:

template<size_t N>
void function(char const (&string)[N]);

Don't forget that the size N includes the null-terminator.

like image 112
Some programmer dude Avatar answered Sep 29 '22 20:09

Some programmer dude


Thanks to everyone's investigations here, it sparked some ideas of my own and I think I found a satisfactory solution. The key is to use template overrides, which was a hint from someone else in this thread. Thanks to everyone for the help. Here's the solution:

template<typename T1, typename T2>
T1 lexical_cast(T2 const& value, T1 const& defaultValue = T1{})
{
    std::cout << "Generic Conversion\n";
    T1 convertedValue;
    if (!boost::conversion::try_lexical_convert(value, convertedValue))
    {
       return defaultValue;
    }

    return convertedValue;
}

template<>
std::string lexical_cast<std::string, bool>(bool const& value, std::string const& defaultValue)
{
    std::cout << "Specialized bool to string\n";
    std::ostringstream ss;
    if (!(ss << std::boolalpha << value))
    {
        std::cout << "Failed bool to string\n";
        return defaultValue;
    }

    return ss.str();
}

template<typename B>
std::enable_if_t<std::is_same<B, bool>::value, B>
lexical_cast(char const* value, bool defaultValue = {})
{
    std::cout << "Specialized c string to bool\n";
    bool convertedValue;
    std::istringstream ss(value);
    if (!(ss >> std::boolalpha >> convertedValue))
    {
        std::cout << "Failed string to bool\n";
        return defaultValue;
    }

    return convertedValue;
}

template<typename B>
std::enable_if_t<std::is_same<B, bool>::value, B>
lexical_cast(std::string const& value, bool defaultValue = {})
{
    std::cout << "Specialized string to bool\n";
    return lexical_cast<bool>(value.c_str(), defaultValue);
}

template<typename T>
void PrintResult(T const& result)
{
    std::cout << "--- Result: " << result << "\n";
}

int main()
{
    PrintResult(lexical_cast<std::string>(3.14f));
    PrintResult(lexical_cast<float>("3.14"));
    PrintResult(lexical_cast<int>("3.14"));
    PrintResult(lexical_cast<bool>("true"));
    PrintResult(lexical_cast<std::string>(true));

    std::string trueString = "true";
    PrintResult(lexical_cast<bool>(trueString));
}

The output:

Generic Conversion
--- Result: 3.1400001
Generic Conversion
--- Result: 3.14
Generic Conversion
--- Result: 0
Specialized c string to bool
--- Result: 1
Specialized bool to string
--- Result: true
Specialized string to bool
Specialized c string to bool
--- Result: 1

And the live sample here.

like image 42
void.pointer Avatar answered Sep 29 '22 22:09

void.pointer