Parduz
Parduz

Reputation: 602

A variadic function that accepts Strings and Ints, Format the latter and concatenate all?

I'm trying to use the answer from DanielKO in this question for my needs but i'm not familiar with templates and variadic functions, and i don't get what should i do.

What i'd need is a variadic c++(11) function which i can call like this:

 String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);

and have NewMsg= "SET,0010,0001,0011,0000,RED,002F".

I'm not even able to get where should i add the comma between the arguments. And then: How could i distinguish between integers and string while parsing the args, so to format each integer to hexadecimal strings?

Upvotes: 5

Views: 335

Answers (3)

max66
max66

Reputation: 66230

Mixing other two solutions (the recursive one from Passer By and the C++17 fold expression one from Nick Mertin), you can write CreateMessage() without recursion (in a simil-fold expression way) also in C++11

std::string const & getStr (std::string const & ret)

 { return ret; }

std::string getStr (int val)
 {
   std::ostringstream ret;

   ret << std::hex << std::setw(4) << std::setfill('0') << val;

   return ret.str();
 }

template <typename ... Ts>
std::string CreateMessage (Ts const & ... args)
 {
   using unused = int[];

   std::string ret   = "";
   std::string comma = "";

   (void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };

   return ret;
 }

-- EDIT --

The OP asks

Would you like to teach me how that "simil-fold" line works? How should i "read" it?

Well... the line is the following

(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };

where unused is an alias (using) for int[].

It uses the fact that a variadic pack (template or function arguments) can be expanded in the context of a C-style array initialization and also uses the power of the comma operator (the propriety of comma operator of exec/compute and discard what is on the left of the comma).

So you have the expansion (for every argument in args...) of

( ret += comma + getStr(args), comma = ",", 0 )

where

1) you add comma + getStr(args) to ret, where comma is empty for first argument and equal to "," (see (2)) for the following arguments

2) the first comma operator discard the value of ret and assign "," to comma (so you have an empty comma in first ret += and "," for followings ret +=

3) the second comma operator discard the value of comma and return 0 to the initialization of unuses

So ret is incremented with all getStr(args) separated by "," and the unused array is initialized with zeros.

Observe another couple of points:

a) in the array (the un-named unused) initialization list, you have a starting and not variadic related zero ({ 0,); this is necessary in case the args... list is empty, so the line become (void)unsed { 0 };, that is legal, instead of (void)unused { }; (without that zero) that is a syntax error

b) unused is preceded by (void); this isn't strictly necessary but useful to avoid a warning of type "object defined but not used".

Upvotes: 2

Nick Mertin
Nick Mertin

Reputation: 1209

One option is to use recursion with templates, as Passer By did in his answer. However, in my opinion a more elegant solution (if you are able to use C++17 lanugage features) is to use a fold expression to avoid recursion. The expression is expanded such that Append is called directly for each argument, sort of like a for-loop across the arguments evaluated at compile time.

template <class T>
void Append(std::ostringstream &out, T &&arg) {
    out << "," << std::forward<T>(arg);
}

template <class... TArgs>
std::string CreateMessage(TArgs &&...args) {
    std::ostringstream out;
    (Append(out, std::forward<TArgs>(args)), ...);
    return out.str().substr(1);
}

Live demo here.

Upvotes: 3

Passer By
Passer By

Reputation: 21160

You use recursion and function overloading

std::string CreateMessage(int i)
{
    return /* i formatted as hex */;
}

std::string CreateMessage(const char* s)
{
    return s;
}

template<typename T, typename... Ts>
std::string CreateMessage(T t, Ts... ts)
{
    return CreateMessage(t) + "," + CreateMessage(ts...);
}

Upvotes: 8

Related Questions