Header-only variadic console output and runtime input validation in C++ using templates Template IO Module

This is a single-file, header-only input validation with two variadic output functions. One output function simply inserts a new line char after every output and the other one inserts a space; this is the only difference.

The end goal of this project is a convenient, cross-platform IO module, and I thought that the static type-checking and compile-time generation of templates would be the perfect combination of type safety without run-time overhead, with the added bonus of the simplicity of adding a header-only module to an existing project.

Source

#ifndef _INPUT_VALIDATION_HPP
#define _INPUT_VALIDATION_HPP

/** Required STL Header Files for this module:
*  #include &lt;iostream&gt;
*  #include &lt;limits&gt;
*  #include &lt;typeinfo&gt;
*
*/


namespace IO {

    template &lt;typename T&gt;
    void print(const T&amp; input)
    {
        std::cout &lt;&lt; input &lt;&lt; ' ';
    }


    template &lt;typename T, typename... Types&gt;
    void print(const T&amp; firstArg, const Types&amp;... arguments)
    {
        std::cout &lt;&lt; firstArg &lt;&lt; ' ';
        print(arguments...);
    }


    template &lt;typename T&gt;
    void println(const T&amp; input)
    {
        std::cout &lt;&lt; input &lt;&lt; std::endl;
    }


    template &lt;typename T, typename... Types&gt;
    void println(const T&amp; firstArg, const Types&amp;... arguments)
    {
        std::cout &lt;&lt; firstArg &lt;&lt; std::endl;
        println(arguments...);
    }


    template &lt;typename T&gt;
    T get()
    {
        auto IO_status_flag = false;

        T response;
        std::cin &gt;&gt; response;

        do {
            if (std::cin.fail()) {

                IO::print("[Error] Please enter a valid response:");
                std::cin.clear();
                std::cin.ignore(std::numeric_limits&lt;std::streamsize&gt;::max(), '\n');

                std::cin &gt;&gt; response;
            } else {
                IO_status_flag = true;
            }
        } while (IO_status_flag != true);

        return response;
    }

}


#endif /* _INPUT_VALIDATION_HPP */


Test Code

#include &lt;iostream&gt;
#include &lt;limits&gt;
#include &lt;typeinfo&gt;

#include "input-validation.hpp"


int main()
{
    IO::print("Please enter username:");
    auto username = IO::get&lt;std::string&gt;();

    IO::print("Please enter age:");
    auto age = IO::get&lt;int&gt;();

    IO::print("\nName:", username);
    IO::print("\nAge:", age);

    auto array = { "one", "two", "three", "four", "five", "six" };

    IO::println("\n");

    for(auto str : array)
    {
        IO::println(str);
    }

}


Output

Please enter username: Jose
Please enter age: 23

Name: Jose
Age: 23

one
two
three
four
five
six
Comment

First question is why I would use this instead of the standard operator&lt;&lt; that we are all very familiar with?

Code Review

Reserved Identifiers

Identifiers with a prefix underscore and an initial capital letter are reserved in all contexts for the implementations. i.e. You are not allowed to use them.

So _INPUT_VALIDATION_HPP is an illegal identifier (for you).

The rules on leading underscore are complex. Even if you know the rules, most people don't, so avoid at all costs.

If you must, then make it a trailing underscore.

Manipulator support.

You have not provided support for stream manipulators (std::hex etc..).

std::endl or '\n'

Prefer '\n' over std::endl;. The difference is that std::endl also forces a flush. Manually forcing a flush is 99.99% of the time incorrect as the stream will automatically flush at the optimal times; doing it manually like this will severely degrade performance.

Recursive over iterative templates.

You use a recursive template design. Personally I prefer the iterative approach as it is more compact (and in my opinion readable).

// You recursive approach.
template &lt;typename T&gt;
void print(const T&amp; input)
{
    std::cout &lt;&lt; input &lt;&lt; ' ';
}


template &lt;typename T, typename... Types&gt;
void print(const T&amp; firstArg, const Types&amp;... arguments)
{
    std::cout &lt;&lt; firstArg &lt;&lt; ' ';
    print(arguments...);
}


The alternative:

// The iterative approach.
template &lt;typename T, typename... Types&gt;
void print(Types const&amp;... arguments)
{
    auto unused = {0, (std::cout &lt;&lt; arguments &lt;&lt; ' ', 0)...};
}


Get only half-checks.

In the get() function if it fails you drop the rest of the line and try again. This implies that the input is line based. BUT on success you don't check that the input is a whole line; you just check the line prefix.

// SO my code is.
int val = IO::get&lt;int&gt;();


// I type
10Ton


Then the above function accepts the input as good. But in my opinion that input should be considered to be incorrect as Ton is being ignored.