Typesafe scanf-like function with variadic templates I'm hoping to get some feedback on a function with variadic templates that parses a format string and fills in some parameters whose order and types are based on the characters in the format string. Kind of like scanf().

(For the curious, I am trying to make a typesafe version of this function, documentation here.)

I'm pretty experienced with C but the last time I did anything serious with C++ was at university 15 years ago and it seems like a completely different language now.

This toy code works for me and fails in the ways I'd expect it to when I give it bad input. However, I'm not too familiar with the standard library so I have a strong feeling that I may be missing something. Also the code seems overly verbose for what it does.

So here's what I'm hoping to find out:


Is this code idiomatic modern C++?
Am I overlooking useful features in the standard library that would make things easier?
Should I be using pointers in this way, or am I thinking too much in C?
Should I consider another approach entirely, like tscanf(format, paramname1, paramname2) &gt;&gt; param1 &gt;&gt; param2?


I am constrained to use C++11, the code currently does not use Boost.

#include &lt;cstring&gt;
#include &lt;iostream&gt;

struct SomeStruct {
    int val;
};

template&lt;typename T&gt;
bool
assign(const char c, T&amp; ref) {
    return false;
}

template&lt;&gt;
bool
assign(const char c, bool*&amp; ref) {
    if (c != 'b')
        return false;
    *ref = true;
    return true;
}

template&lt;&gt;
bool
assign(const char c, int*&amp; ref) {
    if (c != 'i')
        return false;
    *ref = 42;
    return true;
}

template&lt;&gt;
bool
assign(const char c, SomeStruct&amp; ref) {
    if (c != 'h')
        return false;
    ref.val = 81;
    return true;
}

template&lt;typename T&gt;
bool
tscanf(const char *format, const char *name, T&amp; ref)
{
    if (std::strlen(format) != 1) {
        std::cerr &lt;&lt; "Wrong number of arguments" &lt;&lt; std::endl;
        return false;
    }

    if (!assign(format[0], ref)) {
        std::cerr &lt;&lt; "Wrong type for argument " &lt;&lt; name &lt;&lt; std::endl;
        return false;
    }

    return true;
}

template&lt;typename T, typename... Args&gt;
bool
tscanf(const char *format, const char *name, T&amp; ref, Args... args)
{
    if (!assign(format[0], ref)) {
        std::cerr &lt;&lt; "Wrong type for argument " &lt;&lt; name &lt;&lt; std::endl;
        return false;
    }

    return tscanf(format + 1, args...);
}

int
main(void)
{
    bool b;
    int i;
    SomeStruct h;
    if (!tscanf("hbi",
        "handle", h,
        "switch", &amp;b,
        "num", &amp;i))
        return 1;
    std::cout &lt;&lt; h.val &lt;&lt; ' ' &lt;&lt; b  &lt;&lt; ' ' &lt;&lt; i &lt;&lt; std::endl;
    return 0;
}
So here's what I'm hoping to find out:
  Is this code idiomatic modern C++?


Pretty much.
You use recursion to handle the multiple arguments. But a better technique may be to use the compiler to build objects to handle each argument. Unfortunately the way you have to defined the interface makes this a bit hard.
If we re-consider the interface to make it like C-scanf.

template&lt;typename T&gt;
int parser(char const*&amp; format, char const*&amp; value, T&amp; value)
{
     // Parse format and value.
     // convert the first % in format into value and save in value.
     // return 1 if value was correctly parsed otherwise 0
}

template&lt;typename... Args)
int tscanf(char const* format, char const* value, Args&amp;... args)
{
    // This will call parser() for each argument.
    // Each of the parameters inside {} are guaranteed to be constructed
    // left to right.
    // Because the compiler knows the number of arguments you
    // get a fixed size array that can be used to count the
    // number of parameters that were actually found.
    auto count[] = {parser(format, value, args)...};

    // Probably need a check for unmatched format parts.

    // The return the number of format parameters matched.
    return std::accumulate(std::begin(count), std::end(count), 0);
}



  Am I overlooking useful features in the standard library that would make things easier?


I don't think there is anything you absolutely need.


  Should I be using pointers in this way, or am I thinking too much in C?


NO. Absolutely do not use pointers like that.

Prefer to use references. This way you don't need to check for null. You also express your intent much more clearly so there is no confusion on the ownership of the pointer.


  Should I consider another approach entirely, like tscanf(format, paramname1, paramname2) >> param1 >> param2?


There have been a couple of attempts like this. boost::format springs to mind. They used % rather than &gt;&gt;. But personally I don't see a need for this. BUT big advancements have come from trying the non obvious (things I would not think of). So please experiment.