1

Is there any new facility in C++11 or C++14 that allows us to stop having to use:

TRACE0("one-string-no-args");  

and

TRACE("formatting string with one-or-more args", arg1, arg2, etc);  

Such functions have had to be differentiated by name, because overloading can't distinguish the zero-additional-arguments case from one-or-more-arguments case, to the best of my knowledge.

This of course matters for printf style interfaces, where being able to know that there aren't anymore arguments means you can avoid a class of bugs by not trying to interpret the formatting string as a formatting string, but only as a final output string.

Mordachai
  • 9,412
  • 6
  • 60
  • 112
  • 6
    `void trace(string);` vs `template void trace(string, Ts...)` is perfectly distinguishable for the compiler. – Xeo Oct 08 '13 at 16:52
  • 4
    Yes, **variadic templates** do just that. If you didn't know the name, here it is. A "safe" print function is the typical introductory example for variadic templates. – DanielKO Oct 08 '13 at 16:53
  • 1
    BTW I've never used it but boost has a pretty cool [named function parameter module](http://www.boost.org/doc/libs/1_37_0/libs/parameter/doc/html/index.html) if you really want to take it to the next level – aaronman Oct 08 '13 at 16:54
  • 2
    TRACE looks like a macro –  Oct 08 '13 at 16:56
  • @aaronman: I think you forgot to tell us why named parameters are related to this...? – DanielKO Oct 08 '13 at 16:59
  • @DieterLücking, if he's using the Microsoft implementation of TRACE then yes it's a macro. They do it so that the code can be automatically removed in a Release build. – Mark Ransom Oct 08 '13 at 17:00
  • @DanielKO that's worthy of a complete answer if you can flesh it out. I hadn't heard of variadic templates yet, it's an interesting concept. Is it useful for anything other than a `printf` replacement? – Mark Ransom Oct 08 '13 at 17:02
  • @DanielKO cause their cool – aaronman Oct 08 '13 at 17:18
  • 1
    "Such functions have had to be differentiated by name, because overloading can't distinguish the zero-additional-arguments case from one-or-more-arguments case, to the best of my knowledge." As far as I can tell, overloading in C++98 has no problem distinguishing between a function with one argument from a function with two or more: `void foo(int); template void foo(int, T, ...);` (and of course the template is only needed if you want the type of the second argument to vary as is possible with varargs.) – bames53 Oct 08 '13 at 17:39
  • @MarkRansom: Yes, they have other uses. For example, `std::async` is a variadic macro that accepts a function/functor to act as the entry point of the thread, followed by some arbitrary number of arguments that will be passed to the thread's function when it's invoked. – Jerry Coffin Oct 08 '13 at 17:42
  • @JerryCoffin: `std::async` is a variadic macro? Since when? – DanielKO Oct 08 '13 at 18:29
  • @DanielKO: Oops -- that should read "variadic template". My apologies. – Jerry Coffin Oct 08 '13 at 18:41

3 Answers3

3

You probably just don't know the name of the feature: variadic templates.

Its main use is to deduce a variable number of types from a variable number of arguments; you can either store it all somewhere (like std::make_tuple()), or just open the arguments to use them.

Here's a basic usage:

void print() {}

template<class Head, class... Tail>
void print(Head h, Tail... t)
{
    cout << h << endl;
    print(t...);
}

int main()
{
    print(3, "hello", 4.5);
}

See it in action here: http://ideone.com/VA7YGK

As you can see it looks like functional programming (because it is!), where you match against a rule that splits the list of arguments into a head and a tail, and then invoke itself with one less element.

Another example, from a recent question, where I defined a data structure recursively (short and simple enough to be readable): Multikey map using variadic templates

The std::thread constructor is another example, it takes a variable number of arguments to give them to the function once it starts running in the spawned thread; just about everything new in C++11 that interacts with functions (e.g. std::function) uses variadic templates so they can accept any number of arguments of any type. std::tuple, std::make_tuple() and std::tie() also make use of it.

Searching the web you'll find plenty of more advanced usages. Pay special attention to the rules for the argument expansion, and perfect forwarding.

Community
  • 1
  • 1
DanielKO
  • 4,422
  • 19
  • 29
0

For the TRACE macros (which are typically included in Microsoft's debugging macros), no, the language will not have a "new" method for handling preprocessor macros (it is effectively the same as it has been).

For functions in general, variadic functions have always been supported (e.g. printf).

Zac Howland
  • 15,777
  • 1
  • 26
  • 42
0

This may seem foolish to some - but I have a metric ton of C-style printf code to maintain.

We could rebuild it all using boost's formatting libraries, and perhaps we'll get to it one of these days. However, in the meantime, just being able to distinguish on one argument, or one + one-or-more arguments is a huge step forward.

https://stackoverflow.com/users/365496/bames53 noted that is is possible to do so, and it appears to work (at some possible expense in code bloat, and with the caveat that this is still printf land with all of its pitfalls).

Here is a simple example that does it's job in MFC/C++:

bool Write(const wchar_t * pszText);
template <typename T> bool Write(const wchar_t * pszFormat, T, ...);

Write need not (and should not) call vwsprintf equivalent, while Write<> does do so in order to build the output string before passing it on to Write.

Very elegant. Removes the problem of either only providing the second interface (and then you get printf problems if your one string happens to have an accidental printf format specifier in it), or forcing clients to specify Write() vs. WriteFormat() or similarly do the string construction locally before calling Write().

Here's Write<> defined in terms of Write:

template <typename T> bool SimpleTextFile::Write(const wchar_t * pszFormat, T, ...)
{
    va_list arglist;
    va_start(arglist, pszFormat);
    CStringW buffer;
    buffer.FormatV(pszFormat, arglist);
    va_end(arglist);
    return Write(buffer);
}
Community
  • 1
  • 1
Mordachai
  • 9,412
  • 6
  • 60
  • 112