As you can see, I’m incorrectly using the auto
data type in the log()
function. But I think it demonstrates what I’m after. I’m trying to make a simple cout
wrapper, and not sure how that would look like for taking any data type.
void log(auto str = "DEFAULT LOG", bool b_endl = true)
{
std::cout << str; if (b_endl == true) { std::cout << std::endl; }
}
int main()
{
log("string");
log(3);
log(3.0);
return 0;
}
13
Note: The Asker’s code will work from C++20 onward. Since it does not, the answer is written, mostly, assuming C++20 is not available.
Use a template to resolve the argument type. You lose the ability to have a parameter-less, default arguments-only function call, can’t infer the template argument without an argument, but this is easy to fix with an overload if the ability to call log()
is a hard requirement.
#include <iostream>
#include <string>
template <class TYPE = const char *>
void log(const TYPE & str = "DEFAULT LOG", bool b_endl = true)
// reference to avoid copies and const because no one expects a logging
// function to change the log message
{
std::cout << str;
if (b_endl == true)
{
std::cout << std::endl;
}
}
int main()
{
log("string");
log(3);
log(3.0);
log();
return 0;
}
Sample output:
string
3
3
DEFAULT LOG
Note: You will still receive errors if you pass in an argument that cannot be resolved (eg: log({1,2,3,4});
) or an argument that cannot be printed with <<
(eg: log(std::vector<int>());
. As of C++20 you can get a slightly more understandable error message if the template argument cannot be streamed by employing a concept
:
#include <iostream>
#include <string>
#include <vector>
template<class TYPE>
concept outstreamable = requires(std::ostream & out, TYPE b) {
out << b;
};
template <class TYPE = const char *> requires outstreamable<TYPE>
void log(const TYPE & str = "DEFAULT LOG", bool b_endl = true)
{
std::cout << str;
if (b_endl == true)
{
std::cout << std::endl;
}
}
int main()
{
log("string");
log(3);
log(3.0);
log(std::vector<int>());
return 0;
}
This version lets you know that the requirement TYPE
being outstreamable
was violated, rather than spitting out page after page of templates that were tried and failed to match.
3
#include <iostream>
// General template to handle multiple arguments
template<typename... Args>
void log(Args... args)
{
(std::cout << ... << args) << std::endl;
}
// Specialization or overload for no arguments, providing a default log
void log() {
std::cout << "DEFAULT LOG" << std::endl;
}
int main()
{
log("Logging: ", 1, " and ", 2.5); // Handles multiple types
log("Another log with a single parameter");
log(); // Uses the default log
return 0;
}