Reputation: 61
Let us consider the following function:
static void Print(const Type& type, const std::string& message, const std::string& variable) {
Log(type, message + ": " + variable);
}
I'd like it to pass arbitrary number of variables (I mean std::string &
variable - this holds a variable name) and then send them via Log()
function together and for this reason, I've considered using template variadic function (an overloaded Print()
). I'd define it like this:
template <typename Arg, typename ...Args)
static void Print(const Type& type, const std::string& message,
const Arg& arg, const Args&... args);
and then:
Print(type, message, args...);
Log(type, message + ": " + arg);
Just an idea, this would work most likely like this:
args...
would be passed and Print()
function would be called recursively until there's no arguments left,Log()
function would be called which would basically log it every time.What I would need to do is to somehow remember arg
value but it would require calling Print()
with an additional argument and I don't really like this idea. Do you have any other clues?
Upvotes: 2
Views: 962
Reputation: 66200
It seems to me that the Max Langhof's solution is simple and elegant.
Unfortunately it uses template folding that is available only starting from C++17.
I propose a C++11/C++14 version that, instead template folding, uses the old trick of the initialization of an unused array
template <typename ... Args>
void Print (Type const & type, std::string const & message,
Args const & ... arg)
{
using unused = int[];
std::stringstream strstr;
strstr << message << ": ";
(void)unused { 0, (strstr << arg << ", ", 0)... };
std::string toLog = strstr.str();
// Remove last separator characters.
toLog.erase(toLog.end() - 2, toLog.end());
Log(type, strstr.str());
}
Upvotes: 3
Reputation: 1907
I simplified your example a bit, so assuming I correctly understood what you want to do, you can do one of the 2 following solutions, if the C++17 folds suggested by @Max Langhof are not supported by your compiler.
Both of them work on any type that supports operator+ for doing the correct thing, but are simple to modify if your concat function is something else.
template <typename Arg>
static void Print(const Arg& message, const Arg& arg1)
{
Log(message + ": " + arg1);
}
template <typename Arg, typename... Args>
static void Print(const Arg& message, const Arg& arg1, const Arg& arg2, const Args&... variables)
{
Print(message, arg1 + ", " + arg2, variables...);
}
template <typename Arg, typename... Args>
static void Print2(const Arg& message, const Arg& arg1, const Args&... variables)
{
std::vector<Arg> args = { variables... };
Arg result = std::accumulate(args.begin(), args.end(), arg1, [](const Arg& a, const Arg& b) {
return a + ", " + b;});
Log(message + ": " + result);
}
Be aware that this version will create copies of the arguments within the std::vector, unlike the other solution which will not.
Both examples can be used in the following fashion:
static void Log(const std::string& m)
{
std::cout << m << std::endl;
}
int main()
{
std::string msg = "MyMessage1";
std::string var1 = "Var1";
std::string var2 = "Var2";
std::string var3 = "Var3";
std::string var4 = "Var4";
std::string var5 = "Var5";
Print(msg, var1);
Print(msg, var1, var2);
Print(msg, var1, var2, var3);
Print(msg, var1, var2, var3, var4);
Print(msg, var1, var2, var3, var4, var5);
}
Upvotes: 1
Reputation: 23691
Depending on the desired format, you might be able to get away with a fold expression:
template<class... Args>
void Print(const Type& type, const std::string& message, const Args&... arg)
{
std::stringstream strstr;
strstr << message << ": "; // Or your prefix computation, whatever you want.
((strstr << arg << ", "), ...);
std::string toLog = strstr.str();
// Remove last separator characters.
toLog.erase(toLog.end() - 2, toLog.end());
Log(type, strstr.str());
}
Upvotes: 3