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
}
}
Related
(Title edited to clarify intent.
Original Title: "Can I heap-allocate a va_list to use it later?")
I'm trying to develop a scripting system for my game that allows me to look at all functions which will be called on a future frame and what arguments they will take. Since the args these functions take aren't all the same, I figured the best approach would be to make a struct containing an unwrapper function that takes a va_list, and a va_list to be passed to it. Each unwrapper function just takes the va_list, separates it back into multiple variables, and passes it to a corresponding real function. Then each script has a queue of those structs to be executed on a given frame.
Here's the script class:
class MoveScript {
public:
MoveScript();
MoveScript(std::string name, std::function<void()> move_script);
void activate(); //Clear the ScriptFrame queue, then call move_script()
void execute(float frame); //Checks if the front ScriptFrame is set to this frame. If it is, then call its execute function and remove it from the queue
std::string name;
std::queue<ScriptFrame> frames;
private:
std::function<void()> move_script{ []() {} }; //Runs whenever a new script is activated, populates the ScriptFrame queue
};
Here's the ScriptFrame class:
class ScriptFrame {
public:
ScriptFrame();
ScriptFrame(float frame);
void execute(); //Executes each function_call in the queue with its corresponding function_arg
float frame;
std::queue<std::function<void(ScriptArg)>> function_calls;
std::queue<ScriptArg> function_args;
};
Here's a ScriptArg:
struct ScriptArg {
ScriptArg();
ScriptArg(int num_args, std::va_list va);
void destroy(); //Call va_end() so I don't have to do it within the unwrapper funcs
int num_args;
std::va_list va;
};
And here's an example of all of this in practice
script("wait", [this]() {
execute_frame(0, [this]() {
push_function(&BattleObject::SET_RATE, 1, 0.5);
});
});
void BattleObject::SET_RATE(ScriptArg args) {
UNWRAP(rate, float);
set_rate(rate);
}
Where script initializes a MoveScript and adds it to a table, execute_frame creates a new ScriptFrame to execute on the given frame, push_function pushes the unwrapper function and a va_list generated by the args to the ScriptFrame's respective queues, and UNWRAP is a macro that expands to float rate = va_arg(args.va, float);
As I’m sure you can already tell, this code doesn’t work because of how a va_list becomes invalid after the function which called va_start returns. I found another question about it but couldn’t find any suggestions that I figured could work. I’ve seen stuff about storing a void* instead of va_list and getting the args directly within push_function, but since the args passed can be of different types, I don’t think there’s any way for me to safely use va_arg at that point. So I was wondering, if I heap-allocate the va_list, will that allow me to store it properly, or does it become invalidated as soon as the function which takes the ellipsis goes out of scope? If it does, can I possibly store the ellipsis directly instead of a va_list, then create a va_list in the unwrapper function? Or just generally if there’s a better way to accomplish my goal, what would that be? Let me know if there’s any other info I should provide.
Thanks!
Update: I tried tuples to store the data as a few answers suggested, but that didn't work because I needed to be able to properly index them while the function was still being wrapped, at which point the number and types of args were still unknown. Due to how templates work, I couldn't find a way to store the entire tuple in the ScriptArg struct either (Note: It's entirely possible that all of this was possible to work around and I just couldn't figure it out, but either way I found a way to handle it without using tuples which works for my purposes).
In any case, I managed to accomplish what I was looking for using a queue of std::any instead of a va_list, and populating the queue with all of the variadic arguments the function took. Handling the variadic args using a tuple still didn't seem to work, but as it turns out, I could just use a vector instead. Here's what the full process looks like.
ScriptArg declaration:
struct ScriptArg {
ScriptArg();
ScriptArg(int num_args, std::queue<std::any> args);
std::any get_arg(); //Get the argument at the front of the queue, pop it and return the argument
void pop();
int num_args;
std::queue<std::any> args;
};
The UNWRAP macros:
//Declares a variable of the given type and extracts its value from the queue
#define UNWRAP(var_name, type) type var_name = std::any_cast<type>(args.get_arg());
//Same as the above, but for variables that were already declared
#define UNWRAP_NO_DECL(var_name, type) var_name = std::any_cast<type>(args.get_arg());
Wrapping a member function:
template<typename ...T>
void push_function(void (BattleObject::* function)(ScriptArg), T... args) {
std::queue<std::any> queue = extract_variadic_to_queue(args...);
ScriptArg sa(sizeof...(args), queue);
active_script_frame.function_calls.push(std::bind(function, this, std::placeholders::_1)); //I believe I could also just bind the arguments directly instead of putting a placeholder here, but since I need to store the arguments separately for other reasons, this is fine.
active_script_frame.function_args.push(sa);
}
template<typename ...T>
std::queue<std::any> extract_variadic_to_queue_impl(const T&... args) {
std::vector<std::any> vec = { args... };
std::queue<std::any> ret;
for (int i = 0, max = vec.size(); i < max; i++) {
ret.push(vec[i]);
}
return ret;
}
template<typename ...T>
std::queue<std::any> extract_variadic_to_queue(T... args) {
return extract_variadic_to_queue_impl(std::any(args)...);
}
And besides the fact that I need to unwrap the float as a double due to std::any's type promotion, the unwrapping function itself looks exactly the same:
void BattleObject::SET_RATE(ScriptArg args) {
UNWRAP(rate, double);
set_rate(rate);
}
I'm still not sure if there's a more efficient way to handle this, but it's overall the simplest method I could find for storing future function arguments without needing to know their quantities and type until they were actually called, which was my end goal. Sorry for not making that more clear in the initial question, but I appreciate you all steering me in the right direction and away from va_lists. Thanks!
I write a command interpreter that parses a command and its arguments and I would like to find a way to pass them to various non-variadic functions like this:
typedef boolean (*CommandExecuteCallback)(char* arg1, ...); // n argument variadic function
char command[CMD_WORD_MAXLEN+1]; // currently parsed command
char args[CMD_MAX_ARGUMENTS][CMD_WORD_MAXLEN+1]; // currently parsed arguments
const char *commands[CMD_MAX_COMMANDS]; // available commands
CommandExecuteCallback commandf[CMD_MAX_COMMANDS]; // available execution functions
executeCommand(char *buf)
{
// find command and parse args
// call it
commandf[i](this->args);
}
bool cmd_blink(char* onOff) { ... }
bool cmd_something(char* arg1, char* arg2) { ... }
I am not sure this is possible in C/C++ and definitely not documented here:
https://en.cppreference.com/w/cpp/utility/variadic
UPDATE:
Variadic functions cannot be dynamically called in C. The solution is to pass the args as array ´char args[][]´ or as std::vector (std:: is too big for Microcontrollers so the 1st solution prevails) which makes the function signatures match. Thanks JoJo and fabian for the leading hints. I will post the solution below.
RELATED:
Forward an invocation of a variadic function in C
This is the code I was looking for. Register n-functions with same signature and variable argument lists (all of type char*). Of course this could be enhanced by dynamically managing commands and arguments, but that's not the tricky part and left to the reader. I can't use std:: because it needs to run on an Microcontroller with limited program memory. But other solutions could use std::
typedef boolean (*CommandExecuteCallback)(char args[CMD_MAX_ARGUMENTS][CMD_WORD_MAXLEN+1]);
const char *commands[CMD_MAX_COMMANDS]; // available commands
CommandExecuteCallback commandF[CMD_MAX_COMMANDS]; // available execution functions
char command[CMD_WORD_MAXLEN+1]; // currently parsed command buffer
char args[CMD_MAX_ARGUMENTS][CMD_WORD_MAXLEN+1]; // currently parsed arguments
interpretCommand()
{
// parse command and args
// find i for command
commandF[i](args); // call the command
}
The Goal:
decide during runtime which templated function to use and then use it later without needing the type information.
A Partial Solution:
for functions where the parameter itself is not templated we can do:
int (*func_ptr)(void*) = &my_templated_func<type_a,type_b>;
this line of code can be modified for use in an if statement with different types for type_a and type_b thus giving us a templated function whose types are determined during runtime:
int (*func_ptr)(void*) = NULL;
if (/* case 1*/)
func_ptr = &my_templated_func<int, float>;
else
func_ptr = &my_templated_func<float, float>;
The Remaining Problem:
How do I do this when the parameter is a templated pointer?
for example, this is something along the lines of what I would like to do:
int (*func_ptr)(templated_struct<type_a,type_b>*); // This won't work cause I don't know type_a or type_b yet
if (/* case 1 */) {
func_ptr = &my_templated_func<int,float>;
arg = calloc(sizeof(templated_struct<int,float>, 1);
}
else {
func_ptr = &my_templated_func<float,float>;
arg = calloc(sizeof(templated_struct<float,float>, 1);
}
func_ptr(arg);
except I would like type_a, and type_b to be determined during runtime. I see to parts to the problem.
What is the function pointers type?
How do I call this function?
I think I have the answer for (2): simply cast the parameter to void* and the template function should do an implicit cast using the function definition (lease correct me if this won't work as I think it will).
(1) is where I am getting stuck since the function pointer must include the parameter types. This is different from the partial solution because for the function pointer definition we were able to "ignore" the template aspect of the function since all we really need is the address of the function.
Alternatively there might be a much better way to accomplish my goal and if so I am all ears.
Thanks to the answer by #Jeffrey I was able to come up with this short example of what I am trying to accomplish:
template <typename A, typename B>
struct args_st {
A argA;
B argB;
}
template<typename A, typename B>
void f(struct args_st<A,B> *args) {}
template<typename A, typename B>
void g(struct args_st<A,B> *args) {}
int someFunction() {
void *args;
// someType needs to know that an args_st struct is going to be passed
// in but doesn't need to know the type of A or B those are compiled
// into the function and with this code, A and B are guaranteed to match
// between the function and argument.
someType func_ptr;
if (/* some runtime condition */) {
args = calloc(sizeof(struct args_st<int,float>), 1);
f((struct args_st<int,float> *) args); // this works
func_ptr = &g<int,float>; // func_ptr should know that it takes an argument of struct args_st<int,float>
}
else {
args = calloc(sizeof(struct args_st<float,float>), 1);
f((struct args_st<float,float> *) args); // this also works
func_ptr = &g<float,float>; // func_ptr should know that it takes an argument of struct args_st<float,float>
}
/* other code that does stuff with args */
// note that I could do another if statement here to decide which
// version of g to use (like I did for f) I am just trying to figure out
// a way to avoid that because the if statement could have a lot of
// different cases similarly I would like to be able to just write one
// line of code that calls f because that could eliminate many lines of
// (sort of) duplicate code
func_ptr(args);
return 0; // Arbitrary value
}
Can't you use a std::function, and use lambdas to capture everything you need? It doesn't appear that your functions take parameters, so this would work.
ie
std::function<void()> callIt;
if(/*case 1*/)
{
callIt = [](){ myTemplatedFunction<int, int>(); }
}
else
{
callIt = []() {myTemplatedFunction<float, float>(); }
}
callIt();
If I understand correctly, What you want to do boils down to:
template<typename T>
void f(T)
{
}
int somewhere()
{
someType func_ptr;
int arg = 0;
if (/* something known at runtime */)
{
func_ptr = &f<float>;
}
else
{
func_ptr = &f<int>;
}
func_ptr(arg);
}
You cannot do that in C++. C++ is statically typed, the template types are all resolved at compile time. If a construct allowed you to do this, the compiler could not know which templates must be instanciated with which types.
The alternatives are:
inheritance for runtime polymorphism
C-style void* everywhere if you want to deal yourself with the underlying types
Edit:
Reading the edited question:
func_ptr should know that it takes an argument of struct args_st<float,float>
func_ptr should know that it takes an argument of struct args_st<int,float>
Those are incompatible. The way this is done in C++ is by typing func_ptr accordingly to the types it takes. It cannot be both/all/any.
If there existed a type for func_ptr so that it could take arguments of arbitrary types, then you could pass it around between functions and compilation units and your language would suddenly not be statically typed. You'd end up with Python ;-p
Maybe you want something like this:
#include <iostream>
template <typename T>
void foo(const T& t) {
std::cout << "foo";
}
template <typename T>
void bar(const T& t) {
std::cout << "bar";
}
template <typename T>
using f_ptr = void (*)(const T&);
int main() {
f_ptr<int> a = &bar<int>;
f_ptr<double> b = &foo<double>;
a(1);
b(4.2);
}
Functions taking different parameters are of different type, hence you cannot have a f_ptr<int> point to bar<double>. Otherwise, functions you get from instantiating a function template can be stored in function pointers just like other functions, eg you can have a f_ptr<int> holding either &foo<int> or &bar<int>.
Disclaimer: I have already provided an answer that directly addresses the question. In this answer, I would like to side-step the question and render it moot.
As a rule of thumb, the following code structure is an inferior design in most procedural languages (not just C++).
if ( conditionA ) {
// Do task 1A
}
else {
// Do task 1B
}
// Do common tasks
if ( conditionA ) {
// Do task 2A
}
else {
// Do task 2B
}
You seem to have recognized the drawbacks in this design, as you are trying to eliminate the need for a second if-else in someFunction(). However, your solution is not as clean as it could be.
It is usually better (for code readability and maintainability) to move the common tasks to a separate function, rather than trying to do everything in one function. This gives a code structure more like the following, where the common tasks have been moved to the function foo().
if ( conditionA ) {
// Do task 1A
foo( /* arguments might be needed */ );
// Do task 2A
}
else {
// Do task 1B
foo( /* arguments might be needed */ );
// Do task 2B
}
As a demonstration of the utility of this rule of thumb, let's apply it to someFunction(). ... and eliminate the need for dynamic memory allocation ... and a bit of cleanup ... unfortunately, addressing that nasty void* is out-of-scope ... I'll leave it up to the reader to evaluate the end result. The one feature I will point out is that there is no longer a reason to consider storing a "generic templated function pointer", rendering the asked question moot.
// Ideally, the parameter's type would not be `void*`.
// I leave that for a future refinement.
void foo(void * args) {
/* other code that does stuff with args */
}
int someFunction(bool condition) {
if (/* some runtime condition */) {
args_st<int,float> args;
foo(&args);
f(&args); // Next step: pass by reference instead of passing a pointer
}
else {
args_st<float,float> args;
foo(&args);
f(&args); // Next step: pass by reference instead of passing a pointer
}
return 0;
}
Your choice of manual memory management and over-use of the keyword struct suggests you come from a C background and have not yet really converted to C++ programming. As a result, there are many areas for improvement, and you might find that your current approach should be tossed. However, that is a future step. There is a learning process involved, and incremental improvements to your current code is one way to get there.
First, I'd like to get rid of the C-style memory management. Most of the time, using calloc in C++ code is wrong. Let's replace the raw pointer with a smart pointer. A shared_ptr looks like it will help the process along.
// Instead of a raw pointer to void, use a smart pointer to void.
std::shared_ptr<void> args;
// Use C++ memory management, not calloc.
args = std::make_shared<args_st<int,float>>();
// or
args = std::make_shared<args_st<float,float>>();
This is still not great, as it still uses a pointer to void, which is rarely needed in C++ code unless interfacing with a library written in C. It is, though, an improvement. One side effect of using a pointer to void is the need for casts to get back to the original type. This should be avoided. I can address this in your code by defining correctly-typed variables inside the if statement. The args variable will still be used to hold your pointer once the correctly-typed variables go out of scope.
More improvements along this vein can come later.
The key improvement I would make is to use the functional std::function instead of a function pointer. A std::function is a generalization of a function pointer, able to do more albeit with more overhead. The overhead is warranted here in the interest of robust code.
An advantage of std::function is that the parameter to g() does not need to be known by the code that invokes the std::function. The old style of doing this was std::bind, but lambdas provide a more readable approach. Not only do you not have to worry about the type of args when it comes time to call your function, you don't even need to worry about args.
int someFunction() {
// Use a smart pointer so you do not have to worry about releasing the memory.
std::shared_ptr<void> args;
// Use a functional as a more convenient alternative to a function pointer.
// Note the lack of parameters (nothing inside the parentheses).
std::function<void()> func;
if ( /* some runtime condition */ ) {
// Start with a pointer to something other than void.
auto real_args = std::make_shared<args_st<int,float>>();
// An immediate function call:
f(real_args.get());
// Choosing a function to be called later:
// Note that this captures a pointer to the data, not a copy of the data.
// Hence changes to the data will be reflected when this is invoked.
func = [real_args]() { g(real_args.get()); };
// It's only here, as real_args is about to go out of scope, where
// we lose the type information.
args = real_args;
}
else {
// Similar to the above, so I'll reduce the commentary.
auto real_args = std::make_shared<args_st<float,float>>();
func = [real_args]() { g(real_args.get()); };
args = real_args;
}
/* other code that does stuff with args */
/* This code is probably poor C++ style, but that can be addressed later. */
// Invoke the function.
func();
return 0;
}
Your next step probably should be to do some reading on these features so you understand what this code does. Then you should be in a better position to leverage the power of C++.
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.
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.