Let's say I want to have a function overloaded by two versions like this:
a) void query(const string& s); which makes an SQL query to a server.
b) void query(const string& s,...); which builds a query string given by a format string and arguments to be substituted. Internally, this version looks like (I hide the details to not over-complicate the question):
va_list vargs;
va_start(vargs, s);
// ... call vsnprintf to build the query string
// ... call the first version with the query string
va_end(vargs);
Note that I also want this to work in both MSVC and GCC. Of course, by writing as above, I cannot go for the following call because of ambiguity:
query("...");
To resolve the ambiguity in this case, I have tried several ways, but none of them works:
1) Rewrite them as:
void query(const string& s) {
// ...
}
template<typename Value>
void query(const string& s, Value value,...) {
va_list vargs;
va_start(vargs, s);
// ...
}
This compiles and works fine in MSVC, but GCC complains with a warning:
"second parameter of va_start is not last named argument"
Even if I ignore that warning, it doesn't work. Somehow vargs cannot capture value parameter for whatever I try: va_start(vargs, s) or va_start(vargs, value). It seems to me that GCC always takes only unnamed parameters into vargs no matter what we provide as 2nd parameter to va_start.
2) Rewrite them as
void query(const string& s) {
// ...
}
template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
va_list vargs;
va_start(vargs, s);
// ...
}
Again, this compiles and works with MSVC. But GCC complains with an error that the 2nd version is a variadic template rather than variadic function, and va_start is not allowed to be used there. It seems that va_start in GCC is built-in rather than from library.
Some people can remark that actually in the 2 versions, 2nd version supersedes the 1st one. That means if I remove the 1st version and put it internally into the 2nd, then everything is alright. But I have a good reason to keep the 1st version: I want the calls with just a string to go directly without unneccessarily calling vsnprintf. So please do not suggest me this way.
I have also thought about combining the 1st version into the 2nd, and then internally count the number of given arguments to know how to go. But it doesn't seem to have a standard way to do that. Determining the number of arguments is possible with variadic templates but not with variadic functions. And if I switch into variadic template, I cannot use va_start anymore in GCC.
Hope someone can help!!
I haven't tested this, but wouldn't the following work?
void query_varargs(const string &s, ...) {
va_list vargs;
va_start(vargs, s);
// ...
}
template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
query_varargs(s, ...value);
}
I.e. move the functionality into a different function (query_varargs), then have the variadic template version of query forward to it.
Related
I have a variadic function LogDebug for log writing. Logging happens in two modes.
My application forwards variadic arguments to another variadic function LogDebugEx in most cases hence that path needs to optimize.
To be specific it takes 38% for vsnprintf for some of my requests on callgrind graph. Please note that this function called many times for a single request.
void LogDebug(const char* zFormat, ...)
{
char zDesc[5000];
va_list ap;
va_start(ap, zFormat);
vsnprintf(zDesc, 5000, zFormat, ap); // Need to optimize in remode mode.
va_end(ap);
if (m_logMode == LOG_MODE_LOCAL) // Does not need to optimize this mode.
{
// This mode is not interested.
}
else // m_logMode == LOG_MODE_REMOTE, critical path
{
LogDebugEx("%s", zDesc); // Forwarded to new variadic function
}
}
Question : I need to avoid copying whole argument list to zDesc array before forwarding to LogDebugEx function.
Is there a way i can perfect forward variadic arguments coming to LogDebug into LogDebugEx function?
Any other fancy way to do this would also be fine without changing function calls to LogDebug.
I have C++11 supported compiler GCC 4.9.3.
If we have c++11, why mess around with variadic argument lists?
#include <utility>
extern enum {LOG_MODE_LOCAL, LOG_MODE_REMOTE} m_logMode;
extern void LogDebugEx(const char*, ...);
template<class...Args>
void LogDebug(const char* zFormat, Args&&...args)
{
if (m_logMode == LOG_MODE_LOCAL) // Does not need to optimize this mode.
{
char zDesc[5000];
snprintf(zDesc, 5000, zFormat, args...);
// do what you have to do here
}
else // m_logMode == LOG_MODE_REMOTE, critical path
{
LogDebugEx(zFormat, std::forward<Args>(args)...); // Forwarded to new variadic function
}
}
I'd like to come up with a c++ wrapper function that fully wraps the TraceLoggingWrite macro. TraceLoggingWrite is a macro with variadic parameters. I attempted the following code snippet, but it would encounter compilation errors because it seems like the syntax requires the wrapped function to accept a va_list parameter. If so, is there another way to accomplish this?
void WrapperFunction(String Name, ...)
{
va_list args;
va_start(args, Name);
TraceLoggingWrite(gProvider,
Name,
TraceLoggingInt32(32, "Test"),
args);
va_end(args);
}
You may consider using parameter pack:
template<typename... Ts>
void WrapperFunction(String Name, Ts... args)
{
TraceLoggingWrite(gProvider,
Name,
TraceLoggingInt32(32, "Test"),
args...);
}
However because TraceLoggingWrite is a variadic macro, there might be cases where parameter packs do not work. An alternative would be to create another macro, also variadic, something like this:
#define WrapperMacro(eventName, ...) TraceLoggingWrite(gProvider, eventName, __VA_ARGS__)
Hello Guys so i want to code something in C++ that i have for C# but as there is no params object in C++ i need some help :P
Ok, so here's what i want to do:
static Int32 Procedure(UInt32 address, params Object[] parameters)
{
Int32 length = parameters.Length;
Int32 index = 0;
UInt32 count = 0;
UInt32 Strings = 0;
UInt32 Single = 0;
UInt32 Array = 0;
while (index < length)
{
if (parameters[index] is Int32)
{
WriteInt32(0x10050000 + (count * 4), (Int32)parameters[index]);
count++;
}
else if(paramaters[index] is String){ }.... // Thats just one thing i wanna use.. i've got more
..........
..........
}
return ReadInt32(0x000000);
}
so i need to figure out what type the parameter is + i wanna use an unknown amount of arguments and i have no idea how i would do this xD
I hope its clear and hopefully someone can Help me :3
Thx, Nico!
You can achieve something similar in C++ with variadic templates. Note that since C++ has no runtime reflection, it's not possible to dynamically get the type of any value: it can only be done at compile-time. Importantly, this also means that you cannot build a parameter list at runtime and pass it to the function without rolling out your own stuff to do it.
It is also arguably much more complicated than a C# equivalent (but then again, if C++ had all the strengths of C# with no weaknesses of itself, nobody would be using C#).
There may be other patterns, but the one I usually use looks like this (example with a print function):
template<typename... T>
void print_all_values(int value, T... values)
{
printf("%i ", value);
print_all_values(values...);
}
template<typename... T>
void print_all_values(double value, T... values)
{
printf("%g ", value);
print_all_values(values...);
}
template<typename... T>
void print_all_values(const char* value, T... values)
{
printf("%s ", value);
print_all_values(values...);
}
template<typename Unknown, typename... T>
void print_all_values(Unknown&& value, T... values)
{
printf("(can't print) ");
print_all_values(values...);
}
void print_all_values() {}
print_all_values(4, "hello world", 5.2, nullptr);
// prints: "4 hello world 5.2 (can't print)"
What happens here:
template<typename... T>
void print_all_values
This tells the compiler to create a distinct version of print_all_values for each different parameter type sequences it finds in my program.
void print_all_values(int value, T... values)
void print_all_values(double value, T... values)
void print_all_values(const char* value, T... values)
These differentiate the call per the first parameter. The idea here is that the function will only print its first parameter, then recursively call the template version with the remaining parameters:
{
printf("%s ", value);
print_all_values(values...);
}
At the end of the recursion chain, each parameter has been printed.
For my example print_all_values(4, "hello world", 5.2, nullptr), this is basically what would happen:
print_all_values(4, "hello world", 5.2, nullptr) -> the compiler uses print_all_values(4, ...), at runtime it'll do printf("%i", value), and the call at the end of the function becomes:
print_all_values("hello world", 5.2, nullptr) -> the compiler uses print_all_values("hello world", ...), at runtime it'll do printf("%s", value), and then:
print_all_values(5.2, nullptr) -> the compiler uses print_all_values(5.2, ...), printf("%g", value), then:
print_all_values(5.2, nullptr) -> the compiler can't find a suitable overload, so it falls back to the print_all_values(Unknown&& value, T... values) overload, does "(can't print)", and creates a call to print_all_values(), which does nothing.
The last overload:
template<typename Unknown, typename... T>
void print_all_values(Unknown&& value, T... values)
tells the compiler how handle any unknown type (in this case by printing (can't print)). Without this overload, we'd get a compile-time error if we tried to print an unknown type (because it all happens at compile-time, remember).
Did you already try a variadic template declaration like given in the following sample?
template<typename... Args>
static int32_t Procedure(uint32_t address, Args&&... parameters) {
// ...
}
C++ allows you to write functions accepting any number of parameters in the form of variadic template functions:
template<typename... ARGS>
void f( ARGS... args )
{
}
In that example, ARGS and args denote what is known as variadic packs. Neither are a template parameter or an function parameter, are just something that represents a set of template parameters, and a set of function parameters (Respectively).
So that are not parameters, are parameter packs, and then them cannot be manipulated directly. To use the content of a variadic pack, you have to expand the pack with an ellipsis.
Consider the example above: template<typename... ARGS> declares a variadic template with a variadic-pack named ARGS which represents a set of type template parameters.
In the next line, we expand that pack (ARGS...) to use that types as the types of the function argumments. That generates the variadic pack of function argumments args.
To use that argumments inside the function, you should expand args too. Since a pack is just a indetermined set of argumments, you can only use it in contexts where you use the hole set of argumments, in other words, you cannot access directly an specific element of the pack. For example:
template<typename... ARGS>
void f( ARGS... args )
{
f( args... ); //We call f() expanding the args pack of function parameters
//and passing the set of parameters to the function.
}
If you need to traverse the set of parameters in a pack (Which you would do in C# using the subscript operator on the params), you have to use the functional programming way of pattern matching and head-tail recursive list traversing:
template<typename HEAD , typename... TAIL>
void print( const HEAD& head , const TAIL&... tail )
{
std::cout << head << std::endl; //Do something with the head (Which is a
//normal function parameter)
print( tail... ); //Pass the tail to the next call
}
Note that function expects at least one parameter (A variadic template could be empty, but print() has one non-variadic parameter) . You should provide an overload with no parameters to act as base case (The case when there is no more argumments in the argumments list):
void print()
{
//Base case. Does nothing.
}
Now consider the signature of the print() function: Is a function which can take any number of parameters of any combination of types. In contrast to the C# (And Java) approach, storing the parameters in an array of an universal base class, and rely on polymorphism and casting), the C++ approach uses a statically-typed alternative, where the type of each function parameter is well determined at compile time.
Short version: How can I pass the contents represented by ... in a variable argument function to another function without first parsing it into a va_list?
Long version:
Below are two functions in a class of mine. I would like to draw your attention to the fact that the first four lines of each function are identical. And I have a half dozen other functions in this class with the same first four lines.
void cyclOps::Logger::warn(char* szFile, char* szFunction, int iLine, char* szFormat, ...) {
va_list vaArguments;
va_start(vaArguments, szFormat);
char szOutput[10000];
_vsnprintf_s(szOutput, CYCLOPSSIZEOF(szOutput), _TRUNCATE, szFormat, vaArguments);
this->log("WARNING: %s [%s - %s(%d)]", szOutput, szFile, szFunction, iLine);
}
void cyclOps::Logger::info(char* szFormat, ...) {
va_list vaArguments;
va_start(vaArguments, szFormat);
char szOutput[10000];
_vsnprintf_s(szOutput, CYCLOPSSIZEOF(szOutput), _TRUNCATE, szFormat, vaArguments);
this->log("INFO: %s", szOutput);
}
I would like to put these four identical lines in a single function called summarizeVariableArguments() and call it something like this...
void cyclOps::Logger::info(char* szFormat, ...) {
std::string strOutput = this->summarizeVariableArguments(/* TBD */);
this->log("INFO: %s", strOutput.c_str());
}
...where the contents of strOutput would be the same as the contents of szOutput in the two previous functions. But how do I pass the ... parameter to another function?
You cannot do that portably (or perhaps at compile time, with horrible C++2011 variadic template tricks).
If you want to call at runtime a variadic function, you may want to use the libffi.
Details are operating system, compiler, processor and ABI specific. (but libffi is trying to abstract them).
That's what perfect forwarding is all about + variadic templates.
template<typename ...Args>
void cyclOps::Logger::info(char* szFormat, Args &&...args) {
std::string strOutput = this->summarizeVariableArguments(std::forward<Args>(args)...);
this->log("INFO: %s", strOutput.c_str());
}
You make another function that accepts va_list to do the job like so:
void cyclOps::Logger::vLog(const char* format, va_list args)
{
std::string logMessage = vFormat<10000>(format, args);
// Do what you want with logMessage
}
template <size_t BufferSize>
std::string cyclOps::Logger::vFormat(const char* format, va_list args)
{
char buffer[BufferSize];
vsprintf(buffer, format, args);
return std::string(buffer);
}
I have tested this on MSVC and GCC for my project. All I can say is it works for me.
Here's a working example. This solution works for C++03 and I believe should work with C++11.
I'm running these two lines of code to add to a map later on:
o.add("-i", 800, "int option");
o.add("-w", "'switch on' option (no third parameter)");
To add them i'm using my two add functions defined as:
template<class T>
void add(std::string name, T value, std::string desc);
template<class T>
void add(std::string name, std::string desc);
The first one works fine, and returns the values I want, but if I add the second one, I get the error:
error: no matching function for call to ‘Opt::add(const char [3], const char [40])’
My question is why it's using my strings in the first one properly, and my strings in the second are being thought of as const char arrays.
Thank you in advance.
Since you're not using the template argument in your second overload, remove it:
template<class T>
void add(std::string name, T value, std::string desc);
void add(std::string name, std::string desc);
A working sample can be found here.
The error message is weird, but to use the second overload you need to explicitly specify the template argument (since there's no way to automatically deduce it):
o.add<T>("-w", "'switch on' option (no third parameter)");
Or, if you don't actually need the template parameter in this case, just make it a non-template method.